// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. remote-python-sync module for scripts/src/platform-infra-sub2api-codex.ts. // Moved mechanically from scripts/src/platform-infra-sub2api-codex.ts:4265-5400 for #903. import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs"; import { homedir } from "node:os"; import { join } from "node:path"; import type { UniDeskConfig } from "../config"; import { rootPath } from "../config"; import type { RenderedCliResult } from "../output"; import { applyPk01CaddyBlock, prepareFrpcSecret, renderFrpcManifest, type PublicServiceExposure, type PublicServiceTarget } from "../platform-infra-public-service"; import { shortSha256Fingerprint } from "../platform-infra-ops-library"; import { codexPoolSentinelSummary, codexPoolSentinelRuntimeImage, readCodexPoolSentinelConfig, renderCodexPoolSentinelManifest, type CodexPoolSentinelConfig, type CodexPoolSentinelProfileSecret, } from "../platform-infra-sub2api-codex-sentinel"; import { parseEnvFile, readTextFile, redactRepoPath, requiredEnvValue } from "../secrets"; import { runSshCommandCapture, type SshCaptureResult } from "../ssh"; import type { CodexPoolConfig, CodexPoolRuntimeTarget } from "./types"; import { desiredAccountCapacityMap, desiredAccountLoadFactorMap, desiredAccountTempUnschedulableMap, desiredAccountWebSocketsV2ModeMap } from "./accounts"; import { resolvedManualAccountProtections } from "./public-exposure"; import { fieldManager } from "./types"; function pyJson(value: unknown): string { return `json.loads(${JSON.stringify(JSON.stringify(value))})`; } export function remotePythonScript(mode: "sync" | "validate" | "trace" | "cleanup-probes" | "sentinel-probe", encodedPayload: string, pool: CodexPoolConfig, target: CodexPoolRuntimeTarget): string { const hostDockerEnvPath = target.runtimeMode === "host-docker" ? target.hostDockerEnvPath : null; return ` set -u python3 - <<'PY' import base64 import hashlib import json import os import re import secrets import string import subprocess import sys import time from datetime import datetime, timezone, timedelta from urllib.parse import quote TARGET_ID = ${pyJson(target.id)} RUNTIME_MODE = ${pyJson(target.runtimeMode)} NAMESPACE = ${pyJson(target.namespace)} SERVICE_NAME = ${pyJson(target.serviceName)} SERVICE_DNS = ${pyJson(target.serviceDns)} HOST_DOCKER_APP_PORT = ${pyJson(target.hostDockerAppPort)} HOST_DOCKER_ENV_PATH = ${pyJson(hostDockerEnvPath)} HOST_DOCKER_APP_CONTAINER = "sub2api-app" FIELD_MANAGER = "${fieldManager}" APP_SECRET_NAME = ${pyJson(target.appSecretName)} POOL_GROUP_NAME = "${pool.groupName}" POOL_GROUP_DESCRIPTION = ${pyJson(pool.groupDescription)} POOL_API_KEY_NAME = "${pool.apiKeyName}" POOL_API_KEY_SECRET_NAME = "${pool.apiKeySecretName}" POOL_API_KEY_SECRET_KEY = "${pool.apiKeySecretKey}" POOL_ADMIN_EMAIL_DEFAULT = ${pyJson(pool.adminEmailDefault)} MIN_OWNER_BALANCE_USD = ${pyJson(pool.minOwnerBalanceUsd)} MIN_OWNER_CONCURRENCY = ${pyJson(pool.minOwnerConcurrency)} MIN_OWNER_CONCURRENCY_SOURCE = ${pyJson(pool.minOwnerConcurrencySource)} POOL_DEFAULT_ACCOUNT_PRIORITY = ${pyJson(pool.defaultAccountPriority)} POOL_DEFAULT_ACCOUNT_CAPACITY = ${pyJson(pool.defaultAccountCapacity)} POOL_DEFAULT_ACCOUNT_LOAD_FACTOR = ${pyJson(pool.defaultAccountLoadFactor)} RESPONSES_SMOKE_MODEL = ${pyJson(pool.localCodex.responsesSmokeModel)} EXPECTED_ACCOUNT_CAPACITIES = ${pyJson(desiredAccountCapacityMap(pool))} EXPECTED_ACCOUNT_LOAD_FACTORS = ${pyJson(desiredAccountLoadFactorMap(pool))} EXPECTED_ACCOUNT_WS_MODES = json.loads(${JSON.stringify(JSON.stringify(desiredAccountWebSocketsV2ModeMap(pool)))}) EXPECTED_ACCOUNT_TEMP_UNSCHEDULABLE = json.loads(${JSON.stringify(JSON.stringify(desiredAccountTempUnschedulableMap(pool)))}) MANUAL_ACCOUNT_PROTECTIONS = json.loads(${JSON.stringify(JSON.stringify(resolvedManualAccountProtections(pool, target)))}) SENTINEL_CONFIG = json.loads(${JSON.stringify(JSON.stringify(pool.sentinel))}) TARGET_EGRESS_PROXY = json.loads(${JSON.stringify(JSON.stringify(target.egressProxy))}) TARGET_SENTINEL_ENABLED = ${target.sentinelEnabled ? "True" : "False"} MODE = "${mode}" PAYLOAD_B64 = "${encodedPayload}" def run(cmd, input_bytes=None): return subprocess.run(cmd, input=input_bytes, stdout=subprocess.PIPE, stderr=subprocess.PIPE) def text(data, limit=4000): if isinstance(data, bytes): data = data.decode("utf-8", errors="replace") return data[-limit:] def read_host_env(): if RUNTIME_MODE != "host-docker": return {} if not isinstance(HOST_DOCKER_ENV_PATH, str) or not HOST_DOCKER_ENV_PATH: raise RuntimeError("host-docker env source path missing") values = {} lines = read_host_env_lines() for line in lines: stripped = line.strip() if not stripped or stripped.startswith("#") or "=" not in stripped: continue key, value = stripped.split("=", 1) key = key.strip() value = value.strip() if len(value) >= 2 and value[0] == value[-1] and value[0] in ("'", '"'): value = value[1:-1] if key: values[key] = value return values def read_host_env_lines(): try: with open(HOST_DOCKER_ENV_PATH, "r", encoding="utf-8") as handle: return handle.read().splitlines() except FileNotFoundError: raise RuntimeError(f"host-docker env source missing: {HOST_DOCKER_ENV_PATH}") except PermissionError: proc = run(["sudo", "-n", "cat", HOST_DOCKER_ENV_PATH]) if proc.returncode != 0: raise RuntimeError("read host-docker env source failed: " + text(proc.stderr, 1000)) return proc.stdout.decode("utf-8", errors="replace").splitlines() def write_host_env_value(key, value): if RUNTIME_MODE != "host-docker": raise RuntimeError("write_host_env_value is only valid for host-docker") if not isinstance(HOST_DOCKER_ENV_PATH, str) or not HOST_DOCKER_ENV_PATH: raise RuntimeError("host-docker env source path missing") if not re.match(r"^[A-Za-z_][A-Za-z0-9_]*$", key): raise RuntimeError(f"unsupported env key: {key}") os.makedirs(os.path.dirname(HOST_DOCKER_ENV_PATH), exist_ok=True) try: lines = read_host_env_lines() except RuntimeError as exc: if "missing" not in str(exc): raise lines = [] next_lines = [] replaced = False for line in lines: stripped = line.strip() if stripped.startswith("#") or "=" not in stripped: next_lines.append(line) continue current_key = stripped.split("=", 1)[0].strip() if current_key == key: next_lines.append(f"{key}={value}") replaced = True else: next_lines.append(line) if not replaced: next_lines.append(f"{key}={value}") content = "\\n".join(next_lines).rstrip() + "\\n" tmp_path = HOST_DOCKER_ENV_PATH + ".tmp" try: with open(tmp_path, "w", encoding="utf-8") as handle: handle.write(content) os.chmod(tmp_path, 0o600) os.replace(tmp_path, HOST_DOCKER_ENV_PATH) except PermissionError: try: os.unlink(tmp_path) except Exception: pass script = r''' set -eu path="$1" dir="$(dirname "$path")" mkdir -p "$dir" tmp="$path.tmp.$$" umask 077 cat > "$tmp" mv "$tmp" "$path" chmod 600 "$path" ''' proc = run(["sudo", "-n", "sh", "-c", script, "sh", HOST_DOCKER_ENV_PATH], content.encode("utf-8")) if proc.returncode != 0: raise RuntimeError("write host-docker env source failed: " + text(proc.stderr, 1000)) return "updated" if replaced else "created" def docker(args): proc = run(["docker", *args]) if proc.returncode == 0: return proc sudo_proc = run(["sudo", "-n", "docker", *args]) return sudo_proc if sudo_proc.returncode == 0 else proc def runtime_logs(since, tail): if RUNTIME_MODE == "host-docker": return docker(["logs", f"--since={since}", f"--tail={tail}", HOST_DOCKER_APP_CONTAINER]) return kubectl(["-n", NAMESPACE, "logs", "deployment/sub2api", f"--since={since}", f"--tail={tail}"]) def kubectl(args, input_obj=None): if isinstance(input_obj, str): input_bytes = input_obj.encode("utf-8") else: input_bytes = input_obj return run(["kubectl", *args], input_bytes) def require_kubectl(args, input_obj=None, label="kubectl"): proc = kubectl(args, input_obj) if proc.returncode != 0: raise RuntimeError(f"{label} failed: {text(proc.stderr, 1000)}") return proc.stdout def kube_json(args, label): raw = require_kubectl([*args, "-o", "json"], label=label) return json.loads(raw.decode("utf-8")) def decode_secret_value(name, key): if RUNTIME_MODE == "host-docker": return read_host_env().get(key) data = kube_json(["-n", NAMESPACE, "get", "secret", name], f"secret/{name}").get("data") or {} if key not in data: return None return base64.b64decode(data[key]).decode("utf-8") def get_config_value(name, key): if RUNTIME_MODE == "host-docker": return read_host_env().get(key) data = kube_json(["-n", NAMESPACE, "get", "configmap", name], f"configmap/{name}").get("data") or {} value = data.get(key) return value if isinstance(value, str) and value else None def select_app_pod(): if RUNTIME_MODE == "host-docker": return HOST_DOCKER_APP_CONTAINER pods = kube_json(["-n", NAMESPACE, "get", "pods", "-l", "app.kubernetes.io/name=sub2api"], "sub2api pods").get("items") or [] for pod in pods: status = pod.get("status") or {} if status.get("phase") != "Running": continue statuses = status.get("containerStatuses") or [] if statuses and all(item.get("ready") is True for item in statuses): return pod["metadata"]["name"] if pods: return pods[0]["metadata"]["name"] raise RuntimeError("sub2api app pod not found") APP_POD = select_app_pod() def parse_curl_output(proc): stdout = proc.stdout.decode("utf-8", errors="replace") marker = "\\n__HTTP_CODE__:" pos = stdout.rfind(marker) if pos < 0: return { "ok": False, "httpStatus": 0, "json": None, "body": stdout, "stderr": text(proc.stderr, 1000), "transportExitCode": proc.returncode, } body = stdout[:pos] status_text = stdout[pos + len(marker):].strip() try: http_status = int(status_text[-3:]) except ValueError: http_status = 0 try: parsed = json.loads(body) if body.strip() else None except json.JSONDecodeError: parsed = None return { "ok": proc.returncode == 0 and 200 <= http_status < 300, "httpStatus": http_status, "json": parsed, "body": body, "stderr": text(proc.stderr, 1000), "transportExitCode": proc.returncode, } def utc_iso(offset_seconds=0): return (datetime.now(timezone.utc) + timedelta(seconds=offset_seconds)).strftime("%Y-%m-%dT%H:%M:%SZ") def normalize_runtime_base_url(value): if not isinstance(value, str): return None value = value.strip().rstrip("/") return value or None def empty_to_none(value): return value if isinstance(value, str) and value else None def sentinel_quality_gate_enabled(): return TARGET_SENTINEL_ENABLED and (SENTINEL_CONFIG.get("monitor") or {}).get("enabled") is True and (SENTINEL_CONFIG.get("actions") or {}).get("enabled") is True def account_notes_fingerprint(account): notes = account.get("notes") if isinstance(account, dict) else None if not isinstance(notes, str): return None match = re.search(r"fingerprint=([A-Za-z0-9_-]+)", notes) return match.group(1) if match else None def runtime_account_credentials(account): credentials = account.get("credentials") if isinstance(account, dict) and isinstance(account.get("credentials"), dict) else {} return credentials def runtime_account_extra(account): extra = account.get("extra") if isinstance(account, dict) and isinstance(account.get("extra"), dict) else {} return extra def sentinel_probe_change_reasons(current, profile): if not isinstance(current, dict) or current.get("id") is None: return ["created"] credentials = runtime_account_credentials(current) extra = runtime_account_extra(current) expected_base_url = normalize_runtime_base_url(profile.get("baseUrl")) runtime_base_url = normalize_runtime_base_url(credentials.get("base_url")) expected_user_agent = empty_to_none(profile.get("upstreamUserAgent")) runtime_user_agent = empty_to_none(credentials.get("user_agent")) expected_ws_mode = empty_to_none(profile.get("openaiResponsesWebSocketsV2Mode")) runtime_ws_mode = empty_to_none(extra.get("openai_apikey_responses_websockets_v2_mode")) expected_trust_upstream = profile.get("trustUpstream") is True runtime_trust_upstream = extra.get("unidesk_trust_upstream") is True expected_protect = profile.get("sentinelProtect") if isinstance(profile.get("sentinelProtect"), dict) else {"enabled": False} runtime_protect = extra.get("unidesk_sentinel_protect") if isinstance(extra.get("unidesk_sentinel_protect"), dict) else {"enabled": False} reasons = [] if empty_to_none(extra.get("unidesk_codex_profile")) != profile.get("profile"): reasons.append("profile") if runtime_base_url != expected_base_url: reasons.append("base-url") if account_notes_fingerprint(current) != profile.get("apiKeyFingerprint"): reasons.append("api-key-fingerprint") if runtime_user_agent != expected_user_agent: reasons.append("upstream-user-agent") if runtime_ws_mode != expected_ws_mode: reasons.append("responses-websockets-v2-mode") if runtime_trust_upstream != expected_trust_upstream: reasons.append("trust-upstream") if runtime_protect != expected_protect: reasons.append("sentinel-protect") return reasons def curl_api(method, path, bearer=None, payload=None): body = b"" if payload is None else json.dumps(payload, separators=(",", ":")).encode("utf-8") script = r''' set -eu method="$1" url="$2" token="\${3:-}" tmp="$(mktemp)" trap 'rm -f "$tmp"' EXIT cat > "$tmp" if [ -n "$token" ]; then if [ "$method" = "GET" ] && [ ! -s "$tmp" ]; then curl -sS -w '\\n__HTTP_CODE__:%{http_code}' -X "$method" -H "Authorization: Bearer $token" "$url" else curl -sS -w '\\n__HTTP_CODE__:%{http_code}' -X "$method" -H 'Content-Type: application/json' -H "Authorization: Bearer $token" --data-binary @"$tmp" "$url" fi else if [ "$method" = "GET" ] && [ ! -s "$tmp" ]; then curl -sS -w '\\n__HTTP_CODE__:%{http_code}' -X "$method" "$url" else curl -sS -w '\\n__HTTP_CODE__:%{http_code}' -X "$method" -H 'Content-Type: application/json' --data-binary @"$tmp" "$url" fi fi ''' if RUNTIME_MODE == "host-docker": if not isinstance(HOST_DOCKER_APP_PORT, int): raise RuntimeError("host-docker app port missing") proc = run(["sh", "-c", script, "sh", method, f"http://127.0.0.1:{HOST_DOCKER_APP_PORT}{path}", bearer or ""], body) else: proc = run([ "kubectl", "-n", NAMESPACE, "exec", "-i", APP_POD, "--", "sh", "-c", script, "sh", method, f"http://127.0.0.1:8080{path}", bearer or "", ], body) return parse_curl_output(proc) def envelope_data(parsed): if isinstance(parsed, dict) and "data" in parsed: return parsed.get("data") return parsed def ensure_success(resp, label): parsed = resp.get("json") code = parsed.get("code") if isinstance(parsed, dict) else None if not resp.get("ok") or (code is not None and code != 0): message = parsed.get("message") if isinstance(parsed, dict) else text(resp.get("body", ""), 500) raise RuntimeError(f"{label} failed: http={resp.get('httpStatus')} message={message}") return envelope_data(parsed) def ensure_admin_compliance(token): status_resp = curl_api("GET", "/api/v1/admin/compliance", bearer=token) if status_resp.get("httpStatus") == 404: return { "ok": True, "action": "not-supported-by-runtime", "valuesPrinted": False, } status = ensure_success(status_resp, "get admin compliance") if not isinstance(status, dict): return { "ok": True, "action": "status-unstructured", "valuesPrinted": False, } version = status.get("version") required = status.get("required") is True if not required: return { "ok": True, "action": "already-acknowledged", "version": version, "requiredBefore": False, "requiredAfter": False, "phrasePrinted": False, "valuesPrinted": False, } phrase = status.get("ack_phrase_zh") if isinstance(status.get("ack_phrase_zh"), str) and status.get("ack_phrase_zh") else status.get("ack_phrase_en") language = "zh" if isinstance(status.get("ack_phrase_zh"), str) and status.get("ack_phrase_zh") else "en" if not isinstance(phrase, str) or not phrase: raise RuntimeError("admin compliance acknowledgement phrase missing") accepted = ensure_success( curl_api("POST", "/api/v1/admin/compliance/accept", bearer=token, payload={"phrase": phrase, "language": language}), "accept admin compliance", ) required_after = accepted.get("required") if isinstance(accepted, dict) else None return { "ok": required_after is False, "action": "accepted", "version": version, "requiredBefore": True, "requiredAfter": required_after, "phrasePrinted": False, "valuesPrinted": False, } def extract_items(data): if isinstance(data, list): return data if isinstance(data, dict): if isinstance(data.get("items"), list): return data["items"] for key in ("groups", "accounts", "api_keys", "keys"): if isinstance(data.get(key), list): return data[key] return [] def find_access_token(data): if isinstance(data, dict): for key in ("access_token", "token"): if isinstance(data.get(key), str) and data[key]: return data[key] for value in data.values(): token = find_access_token(value) if token: return token return None def login(): admin_email = get_config_value("sub2api-config", "ADMIN_EMAIL") or POOL_ADMIN_EMAIL_DEFAULT admin_password = decode_secret_value(APP_SECRET_NAME, "ADMIN_PASSWORD") if not admin_password: raise RuntimeError("ADMIN_PASSWORD missing from sub2api-secrets") data = ensure_success(curl_api("POST", "/api/v1/auth/login", payload={"email": admin_email, "password": admin_password}), "admin login") token = find_access_token(data) if not token: raise RuntimeError("admin login response did not contain access_token") compliance = ensure_admin_compliance(token) if compliance.get("ok") is not True: raise RuntimeError("admin compliance acknowledgement failed") return admin_email, token, compliance def group_payload(): return { "name": POOL_GROUP_NAME, "description": POOL_GROUP_DESCRIPTION, "platform": "openai", "rate_multiplier": 1, "is_exclusive": False, "subscription_type": "standard", "allow_messages_dispatch": True, "require_oauth_only": False, "require_privacy_set": False, "rpm_limit": 0, } def list_groups(token): data = ensure_success(curl_api("GET", "/api/v1/admin/groups/all?platform=openai", bearer=token), "list groups") return extract_items(data) def ensure_group(token): existing = next((item for item in list_groups(token) if item.get("name") == POOL_GROUP_NAME), None) payload = group_payload() if existing is None: created = ensure_success(curl_api("POST", "/api/v1/admin/groups", bearer=token, payload=payload), "create group") return created, "created" group_id = existing.get("id") if group_id is None: raise RuntimeError("existing group has no id") payload["status"] = "active" updated = ensure_success(curl_api("PUT", f"/api/v1/admin/groups/{group_id}", bearer=token, payload=payload), "update group") return updated if isinstance(updated, dict) else existing, "updated" def list_accounts_for_group(token, group_id): path = f"/api/v1/admin/accounts?group_id={group_id}&page=1&page_size=500&platform=openai" data = ensure_success(curl_api("GET", path, bearer=token), f"list accounts for group {group_id}") return extract_items(data) def account_group_ids(token, account): if not isinstance(account, dict) or account.get("id") is None: return [] account_id = account.get("id") account_name = account.get("name") ids = [] for group in list_groups(token): group_id = group.get("id") if isinstance(group, dict) else None if group_id is None: continue members = list_accounts_for_group(token, group_id) if any(item.get("id") == account_id or item.get("name") == account_name for item in members if isinstance(item, dict)): ids.append(group_id) return sorted(set(ids)) def list_accounts(token): path = "/api/v1/admin/accounts?page=1&page_size=200&platform=openai&type=apikey&search=" + quote("unidesk-codex-") data = ensure_success(curl_api("GET", path, bearer=token), "list accounts") return extract_items(data) def find_account_by_name(token, name): path = "/api/v1/admin/accounts?page=1&page_size=50&platform=openai&search=" + quote(str(name)) data = ensure_success(curl_api("GET", path, bearer=token), "find account " + str(name)) for item in extract_items(data): if isinstance(item, dict) and item.get("name") == name: return item return None def list_proxy_candidates(token, search=""): path = "/api/v1/admin/proxies?page=1&page_size=200" if search: path += "&search=" + quote(str(search)) data = ensure_success(curl_api("GET", path, bearer=token), "list proxies") return extract_items(data) def find_proxy_by_name(token, name): for item in list_proxy_candidates(token, name): if isinstance(item, dict) and item.get("name") == name: return item return None def manual_binding_plan(binding, expected_kind): if not isinstance(binding, dict): return None plan = binding.get("sourcePlan") if not isinstance(plan, dict): raise RuntimeError("manual account binding is missing resolved sourcePlan") if plan.get("enabled") is not True: raise RuntimeError(f"manual account binding source {plan.get('id')} is disabled") if plan.get("kind") != expected_kind: raise RuntimeError(f"manual account binding source {plan.get('id')} kind must be {expected_kind}") return plan def desired_manual_proxy_payload(protection): binding = protection.get("proxyBinding") if isinstance(protection, dict) else None if not isinstance(binding, dict) or binding.get("enabled") is not True: return None plan = manual_binding_plan(binding, "proxy") if plan.get("provider") not in ("target-egress-proxy", "fixed-http-proxy"): raise RuntimeError(f"manual account proxyBinding source {plan.get('id')} uses unsupported provider {plan.get('provider')}") if plan.get("provider") == "fixed-http-proxy": fixed_proxy = plan.get("fixedProxy") if not isinstance(fixed_proxy, dict): raise RuntimeError(f"manual account proxyBinding source {plan.get('id')} requires fixedProxy") proxy_name = binding.get("proxyName") protocol = fixed_proxy.get("protocol") host = fixed_proxy.get("host") port = fixed_proxy.get("port") if not isinstance(proxy_name, str) or not proxy_name: raise RuntimeError("manual account proxyBinding proxyName is missing") if protocol != "http": raise RuntimeError("fixed proxy protocol must be http") if not isinstance(host, str) or not host: raise RuntimeError("fixed proxy host is missing") if not isinstance(port, int) or port <= 0: raise RuntimeError("fixed proxy port is missing") return { "name": proxy_name, "protocol": protocol, "host": host, "port": port, "fallback_mode": "none", "expiry_warn_days": 0, } target_proxy = plan.get("targetEgressProxy") if not isinstance(target_proxy, dict): raise RuntimeError(f"manual account proxyBinding source {plan.get('id')} requires an enabled target egressProxy") service_name = target_proxy.get("serviceName") listen_port = target_proxy.get("listenPort") namespace = target_proxy.get("namespace") if isinstance(target_proxy.get("namespace"), str) and target_proxy.get("namespace") else NAMESPACE proxy_name = binding.get("proxyName") if not isinstance(service_name, str) or not service_name: raise RuntimeError("target egressProxy serviceName is missing") if not isinstance(listen_port, int) or listen_port <= 0: raise RuntimeError("target egressProxy listenPort is missing") if not isinstance(proxy_name, str) or not proxy_name: raise RuntimeError("manual account proxyBinding proxyName is missing") return { "name": proxy_name, "protocol": "http", "host": f"{service_name}.{namespace}.svc.cluster.local", "port": listen_port, "fallback_mode": "none", "expiry_warn_days": 0, } def proxy_needs_update(proxy, payload): if not isinstance(proxy, dict): return True for key in ("name", "protocol", "host", "port", "fallback_mode", "expiry_warn_days"): if proxy.get(key) != payload.get(key): return True return proxy.get("status") != "active" def ensure_manual_proxy(token, payload): current = find_proxy_by_name(token, payload["name"]) if current is None: created = ensure_success(curl_api("POST", "/api/v1/admin/proxies", bearer=token, payload=payload), f"create proxy {payload['name']}") return created, "created" if proxy_needs_update(current, payload): update_payload = dict(payload) update_payload["status"] = "active" updated = ensure_success(curl_api("PUT", f"/api/v1/admin/proxies/{current['id']}", bearer=token, payload=update_payload), f"update proxy {payload['name']}") return updated if isinstance(updated, dict) else current, "updated" return current, "unchanged" def manual_proxy_status(token, account, protection): payload = desired_manual_proxy_payload(protection) if payload is None: return { "enabled": False, "ok": True, "action": "not-configured", "valuesPrinted": False, } binding = protection.get("proxyBinding") if isinstance(protection, dict) else None plan = manual_binding_plan(binding, "proxy") proxy = find_proxy_by_name(token, payload["name"]) runtime_proxy = account.get("proxy") if isinstance(account, dict) and isinstance(account.get("proxy"), dict) else None binding_aligned = ( isinstance(account, dict) and isinstance(proxy, dict) and account.get("proxy_id") == proxy.get("id") ) return { "enabled": True, "ok": binding_aligned, "action": "validate", "source": plan.get("id"), "sourceProvider": plan.get("provider"), "expectedProxyName": payload["name"], "expectedProtocol": payload["protocol"], "expectedHost": payload["host"], "expectedPort": payload["port"], "proxyRecordExists": isinstance(proxy, dict), "proxyId": proxy.get("id") if isinstance(proxy, dict) else None, "runtimeProxyId": account.get("proxy_id") if isinstance(account, dict) else None, "runtimeProxyName": runtime_proxy.get("name") if isinstance(runtime_proxy, dict) else None, "bindingAligned": binding_aligned, "valuesPrinted": False, } def ensure_manual_account_proxy_bindings(token): items = [] for protection in MANUAL_ACCOUNT_PROTECTIONS: if not isinstance(protection, dict): continue name = protection.get("accountName") if not isinstance(name, str) or not name: continue account = find_account_by_name(token, name) payload = desired_manual_proxy_payload(protection) if payload is None: items.append({ "accountName": name, "enabled": False, "action": "not-configured", "ok": True, "valuesPrinted": False, }) continue if not isinstance(account, dict): items.append({ "accountName": name, "enabled": True, "action": "account-missing", "ok": False, "expectedProxyName": payload["name"], "valuesPrinted": False, }) continue proxy, proxy_action = ensure_manual_proxy(token, payload) binding = protection.get("proxyBinding") if isinstance(protection, dict) else None plan = manual_binding_plan(binding, "proxy") proxy_id = proxy.get("id") if isinstance(proxy, dict) else None if proxy_id is None: raise RuntimeError(f"proxy {payload['name']} has no id") action = "unchanged" if account.get("proxy_id") != proxy_id: updated = ensure_success(curl_api("PUT", f"/api/v1/admin/accounts/{account['id']}", bearer=token, payload={"proxy_id": proxy_id}), f"bind manual account proxy {name}") account = updated if isinstance(updated, dict) else account action = "bound" runtime_proxy = account.get("proxy") if isinstance(account.get("proxy"), dict) else None items.append({ "accountName": name, "accountId": account.get("id"), "enabled": True, "action": action, "proxyAction": proxy_action, "source": plan.get("id"), "sourceProvider": plan.get("provider"), "ok": account.get("proxy_id") == proxy_id, "expectedProxyName": payload["name"], "expectedProtocol": payload["protocol"], "expectedHost": payload["host"], "expectedPort": payload["port"], "proxyId": proxy_id, "runtimeProxyId": account.get("proxy_id"), "runtimeProxyName": runtime_proxy.get("name") if isinstance(runtime_proxy, dict) else None, "bindingAligned": account.get("proxy_id") == proxy_id, "controlPolicy": "manual-protected: only proxy_id binding is YAML-controlled; credentials/status/schedulable are untouched", "valuesPrinted": False, }) return { "ok": all(item.get("ok") is True for item in items), "itemCount": len(items), "items": items, "valuesPrinted": False, } def manual_group_binding_plan(protection): binding = protection.get("groupBinding") if isinstance(protection, dict) else None if not isinstance(binding, dict) or binding.get("enabled") is not True: return None plan = manual_binding_plan(binding, "group") if plan.get("provider") != "pool-group": raise RuntimeError(f"manual account groupBinding source {plan.get('id')} uses unsupported provider {plan.get('provider')}") return plan def manual_group_binding_enabled(protection): return manual_group_binding_plan(protection) is not None def manual_group_status(token, account, protection, group_id): plan = manual_group_binding_plan(protection) if plan is None: return { "enabled": False, "ok": True, "action": "not-configured", "valuesPrinted": False, } group_accounts = list_accounts_for_group(token, group_id) account_id = account.get("id") if isinstance(account, dict) else None account_name = account.get("name") if isinstance(account, dict) else None binding_aligned = any( item.get("id") == account_id or item.get("name") == account_name for item in group_accounts if isinstance(item, dict) ) return { "enabled": True, "ok": binding_aligned, "action": "validate", "source": plan.get("id"), "sourceProvider": plan.get("provider"), "poolGroupName": POOL_GROUP_NAME, "poolGroupId": group_id, "bindingAligned": binding_aligned, "valuesPrinted": False, } def ensure_manual_account_group_bindings(token, group_id): items = [] for protection in MANUAL_ACCOUNT_PROTECTIONS: if not isinstance(protection, dict): continue name = protection.get("accountName") if not isinstance(name, str) or not name: continue if not manual_group_binding_enabled(protection): items.append({ "accountName": name, "enabled": False, "action": "not-configured", "ok": True, "valuesPrinted": False, }) continue plan = manual_group_binding_plan(protection) account = find_account_by_name(token, name) if not isinstance(account, dict): items.append({ "accountName": name, "enabled": True, "action": "account-missing", "ok": False, "poolGroupName": POOL_GROUP_NAME, "poolGroupId": group_id, "valuesPrinted": False, }) continue existing_group_ids = account_group_ids(token, account) desired_group_ids = sorted(set(existing_group_ids + [group_id])) action = "unchanged" if group_id not in existing_group_ids: updated = ensure_success(curl_api("PUT", f"/api/v1/admin/accounts/{account['id']}", bearer=token, payload={"group_ids": desired_group_ids}), f"bind manual account group {name}") account = updated if isinstance(updated, dict) else account action = "bound" binding_aligned = any( item.get("id") == account.get("id") or item.get("name") == name for item in list_accounts_for_group(token, group_id) if isinstance(item, dict) ) items.append({ "accountName": name, "accountId": account.get("id"), "enabled": True, "ok": binding_aligned, "action": action, "source": plan.get("id") if isinstance(plan, dict) else None, "sourceProvider": plan.get("provider") if isinstance(plan, dict) else None, "poolGroupName": POOL_GROUP_NAME, "poolGroupId": group_id, "previousGroupIds": existing_group_ids, "desiredGroupIds": desired_group_ids, "bindingAligned": binding_aligned, "controlPolicy": "manual-protected: only pool group membership is YAML-controlled; credentials/status/schedulable are untouched and sentinel does not probe it", "valuesPrinted": False, }) return { "ok": all(item.get("ok") is True for item in items), "itemCount": len(items), "items": items, "valuesPrinted": False, } def manual_account_protection_status(token, group_id=None): items = [] desired_names = set(EXPECTED_ACCOUNT_CAPACITIES.keys()) for protection in MANUAL_ACCOUNT_PROTECTIONS: if not isinstance(protection, dict): continue name = protection.get("accountName") if not isinstance(name, str) or not name: continue account = find_account_by_name(token, name) extra = account.get("extra") if isinstance(account, dict) and isinstance(account.get("extra"), dict) else {} proxy_status = manual_proxy_status(token, account, protection) group_status = manual_group_status(token, account, protection, group_id) if group_id is not None else {"enabled": False, "ok": True, "action": "not-checked", "valuesPrinted": False} items.append({ "accountName": name, "reason": protection.get("reason") if isinstance(protection.get("reason"), str) else None, "exists": isinstance(account, dict), "accountId": account.get("id") if isinstance(account, dict) else None, "status": account.get("status") if isinstance(account, dict) else None, "schedulable": account.get("schedulable") if isinstance(account, dict) else None, "inYamlProfiles": name in desired_names, "runtimeMarkedUnideskManaged": extra.get("unidesk_managed") is True, "proxyBinding": proxy_status, "groupBinding": group_status, "ok": proxy_status.get("ok") is True and group_status.get("ok") is True, "controlPolicy": "manual-protected: no create/update/prune/probe/freeze; optional proxy_id and pool group membership binding only when configured", "valuesPrinted": False, }) return { "ok": all(item.get("ok") is True for item in items), "protectedCount": len(items), "items": items, "valuesPrinted": False, } def list_probe_accounts(token): path = "/api/v1/admin/accounts?page=1&page_size=200&platform=openai&type=apikey&search=" + quote("unidesk-probe-") data = ensure_success(curl_api("GET", path, bearer=token), "list probe accounts") return [item for item in extract_items(data) if isinstance(item.get("name"), str) and item.get("name").startswith("unidesk-probe-")] def list_probe_groups(token): data = ensure_success(curl_api("GET", "/api/v1/admin/groups/all?platform=openai", bearer=token), "list groups") return [item for item in extract_items(data) if isinstance(item.get("name"), str) and item.get("name").startswith("unidesk-probe-")] def cleanup_probe_resources(token): api_key_results = [] account_results = [] group_results = [] for item in list_user_keys(token): name = item.get("name") key_id = item.get("id") if not isinstance(name, str) or not name.startswith("unidesk-probe-") or key_id is None: continue api_key_results.append(delete_probe_resource(token, "DELETE", f"/api/v1/keys/{key_id}", "api-key")) for item in list_probe_accounts(token): account_id = item.get("id") if account_id is None: continue account_results.append(delete_probe_resource(token, "DELETE", f"/api/v1/admin/accounts/{account_id}", "account")) for item in list_probe_groups(token): group_id = item.get("id") if group_id is None: continue group_results.append(delete_probe_resource(token, "DELETE", f"/api/v1/admin/groups/{group_id}", "group")) return { "ok": all(item.get("ok") is True for item in api_key_results + account_results + group_results), "apiKeysDeleted": len(api_key_results), "accountsDeleted": len(account_results), "groupsDeleted": len(group_results), "items": { "apiKeys": api_key_results, "accounts": account_results, "groups": group_results, }, "valuesPrinted": False, } def temp_unschedulable_credentials(profile): credentials = profile.get("tempUnschedulableCredentials") if not isinstance(credentials, dict): credentials = {} return normalize_temp_unschedulable_credentials(credentials) def account_payload(profile, group_id): extra = { "openai_responses_mode": "force_responses", "unidesk_codex_profile": profile["profile"], "unidesk_managed": True, "unidesk_trust_upstream": profile.get("trustUpstream") is True, "unidesk_sentinel_protect": profile.get("sentinelProtect") if isinstance(profile.get("sentinelProtect"), dict) else {"enabled": False}, } ws_mode = profile.get("openaiResponsesWebSocketsV2Mode") if ws_mode: extra["openai_apikey_responses_websockets_v2_mode"] = ws_mode extra["openai_apikey_responses_websockets_v2_enabled"] = ws_mode != "off" credentials = { "api_key": profile["apiKey"], "base_url": profile["baseUrl"], } upstream_user_agent = profile.get("upstreamUserAgent") if upstream_user_agent: credentials["user_agent"] = upstream_user_agent temp_unschedulable = temp_unschedulable_credentials(profile) if temp_unschedulable["enabled"]: credentials["temp_unschedulable_enabled"] = True credentials["temp_unschedulable_rules"] = temp_unschedulable["rules"] return { "name": profile["accountName"], "notes": f"UniDesk-managed Codex profile {profile['profile']} from {profile['configFile']} and {profile['authFile']}; secret source={profile['apiKeySource']}; fingerprint={profile['apiKeyFingerprint']}.", "platform": "openai", "type": "apikey", "credentials": credentials, "extra": extra, "concurrency": int(profile.get("capacity", 5) or 5), "priority": int(profile.get("priority", POOL_DEFAULT_ACCOUNT_PRIORITY) or POOL_DEFAULT_ACCOUNT_PRIORITY), "rate_multiplier": 1, "load_factor": int(profile.get("loadFactor", POOL_DEFAULT_ACCOUNT_LOAD_FACTOR) or POOL_DEFAULT_ACCOUNT_LOAD_FACTOR), "group_ids": [group_id], "confirm_mixed_channel_risk": True, **({"proxy_id": int(profile["proxyId"])} if profile.get("proxyId") is not None else {}), } def planned_sentinel_account_results(profiles, existing_accounts): existing = {item.get("name"): item for item in existing_accounts if isinstance(item, dict)} results = [] for profile in profiles: change_reasons = sentinel_probe_change_reasons(existing.get(profile["accountName"]), profile) quality_gate_required = sentinel_quality_gate_enabled() and len(change_reasons) > 0 results.append({ "profile": profile["profile"], "accountName": profile["accountName"], "profileConfig": { "accountName": profile["accountName"], "trustUpstream": profile.get("trustUpstream") is True, "sentinelProtect": profile.get("sentinelProtect") if isinstance(profile.get("sentinelProtect"), dict) else {"enabled": False}, }, "sentinelProbeConfigFingerprint": profile.get("sentinelProbeConfigFingerprint"), "sentinelProbeRequired": quality_gate_required, "sentinelChangeReasons": change_reasons if quality_gate_required else [], "sentinelProbePending": quality_gate_required, "sentinelDefaultFrozen": False, "valuesPrinted": False, }) return results def ensure_accounts(token, profiles, group_id, prune_removed=False, protected_frozen_names=None, existing_accounts=None): if not isinstance(protected_frozen_names, set): protected_frozen_names = set() if not isinstance(existing_accounts, list): existing_accounts = list_accounts(token) existing = {item.get("name"): item for item in existing_accounts} desired_names = {profile["accountName"] for profile in profiles} results = [] for profile in profiles: payload = account_payload(profile, group_id) current = existing.get(profile["accountName"]) change_reasons = sentinel_probe_change_reasons(current, profile) quality_gate_required = sentinel_quality_gate_enabled() and len(change_reasons) > 0 keep_frozen = profile["accountName"] in protected_frozen_names if current and current.get("id") is not None: account_id = current["id"] update_payload = dict(payload) update_payload.pop("platform", None) update_payload["status"] = "active" data = ensure_success(curl_api("PUT", f"/api/v1/admin/accounts/{account_id}", bearer=token, payload=update_payload), f"update account {profile['accountName']}") action = "updated" else: data = ensure_success(curl_api("POST", "/api/v1/admin/accounts", bearer=token, payload=payload), f"create account {profile['accountName']}") action = "created" results.append({ "profile": profile["profile"], "accountName": profile["accountName"], "profileConfig": { "accountName": profile["accountName"], "trustUpstream": profile.get("trustUpstream") is True, "sentinelProtect": profile.get("sentinelProtect") if isinstance(profile.get("sentinelProtect"), dict) else {"enabled": False}, }, "accountId": data.get("id") if isinstance(data, dict) else None, "action": action, "baseUrl": profile["baseUrl"], "apiKeySource": profile["apiKeySource"], "apiKeyFingerprint": profile["apiKeyFingerprint"], "sentinelProbeConfigFingerprint": profile.get("sentinelProbeConfigFingerprint"), "sentinelProbeRequired": quality_gate_required, "sentinelChangeReasons": change_reasons if quality_gate_required else [], "sentinelProbePending": quality_gate_required, "sentinelDefaultFrozen": False, "sentinelFreezeProtected": keep_frozen, "schedulableControl": "sentinel-marker-restore", "openaiResponsesWebSocketsV2Mode": profile.get("openaiResponsesWebSocketsV2Mode"), "trustUpstream": profile.get("trustUpstream") is True, "priority": int(profile.get("priority", POOL_DEFAULT_ACCOUNT_PRIORITY) or POOL_DEFAULT_ACCOUNT_PRIORITY), "capacity": int(profile.get("capacity", 5) or 5), "loadFactor": int(profile.get("loadFactor", POOL_DEFAULT_ACCOUNT_LOAD_FACTOR) or POOL_DEFAULT_ACCOUNT_LOAD_FACTOR), "runtimeConcurrency": data.get("concurrency") if isinstance(data, dict) else None, "runtimeLoadFactor": data.get("load_factor") if isinstance(data, dict) else None, "runtimeSchedulable": data.get("schedulable") if isinstance(data, dict) else None, "tempUnschedulableConfigured": bool(payload["credentials"].get("temp_unschedulable_enabled")), "tempUnschedulableRuleCount": len(payload["credentials"].get("temp_unschedulable_rules") or []), "upstreamUserAgentConfigured": bool(profile.get("upstreamUserAgent")), "valuesPrinted": False, }) prune_results = prune_removed_accounts(token, existing_accounts, desired_names) if prune_removed else [] return results, prune_results def prune_removed_accounts(token, existing_accounts, desired_names): results = [] for account in existing_accounts: name = account.get("name") account_id = account.get("id") extra = account.get("extra") if isinstance(account.get("extra"), dict) else {} if not isinstance(name, str) or not name.startswith("unidesk-codex-"): continue if extra.get("unidesk_managed") is not True: continue if name in desired_names: continue if account_id is None: raise RuntimeError(f"removed account {name} has no id") ensure_success(curl_api("DELETE", f"/api/v1/admin/accounts/{account_id}", bearer=token), f"delete removed account {name}") results.append({ "accountName": name, "accountId": account_id, "profile": extra.get("unidesk_codex_profile"), "action": "deleted", "reason": "removed-from-yaml", "valuesPrinted": False, }) return results def generate_api_key(): alphabet = string.ascii_letters + string.digits return "sk-unidesk-codex-" + "".join(secrets.choice(alphabet) for _ in range(48)) def existing_sub2api_pool_api_key(token): try: existing = next((item for item in list_user_keys(token) if item.get("name") == POOL_API_KEY_NAME), None) except Exception: return None if not isinstance(existing, dict): return None key = existing.get("key") return key if isinstance(key, str) and key else None def ensure_api_key_secret(group_id, token): existing = None try: existing = decode_secret_value(POOL_API_KEY_SECRET_NAME, POOL_API_KEY_SECRET_KEY) except Exception: existing = None reused = None if existing else existing_sub2api_pool_api_key(token) api_key = existing or reused or generate_api_key() if existing: secret_action = "kept-existing" elif reused: secret_action = "reused-existing-sub2api-key" else: secret_action = "created" if RUNTIME_MODE == "host-docker": env_action = "kept-existing" if existing else write_host_env_value(POOL_API_KEY_SECRET_KEY, api_key) return api_key, secret_action, f"host-docker-env:{env_action};source={HOST_DOCKER_ENV_PATH};key={POOL_API_KEY_SECRET_KEY};valuesPrinted=false" manifest = { "apiVersion": "v1", "kind": "Secret", "metadata": { "name": POOL_API_KEY_SECRET_NAME, "namespace": NAMESPACE, "labels": { "app.kubernetes.io/name": "sub2api", "app.kubernetes.io/part-of": "platform-infra", "app.kubernetes.io/managed-by": "unidesk", "unidesk.ai/secret-purpose": "sub2api-codex-pool-api-key", }, }, "type": "Opaque", "stringData": { POOL_API_KEY_SECRET_KEY: api_key, "GROUP_ID": str(group_id), "GROUP_NAME": POOL_GROUP_NAME, "SERVICE_DNS": SERVICE_DNS, }, } proc = kubectl(["apply", "--server-side", "--force-conflicts", f"--field-manager={FIELD_MANAGER}", "-f", "-"], json.dumps(manifest)) if proc.returncode != 0: raise RuntimeError(f"apply API key secret failed: {text(proc.stderr, 1000)}") return api_key, secret_action, text(proc.stdout, 1000) def pool_api_key_secret_location(): if RUNTIME_MODE == "host-docker": return f"{HOST_DOCKER_ENV_PATH}.{POOL_API_KEY_SECRET_KEY}" return f"{NAMESPACE}/{POOL_API_KEY_SECRET_NAME}.{POOL_API_KEY_SECRET_KEY}" def apply_sentinel_manifest(manifest): if not TARGET_SENTINEL_ENABLED: return { "ok": True, "action": "skipped-target-disabled", "valuesPrinted": False, } if not isinstance(manifest, str) or not manifest.strip(): return { "ok": False, "action": "missing-manifest", "valuesPrinted": False, } proc = kubectl(["apply", "--server-side", "--force-conflicts", f"--field-manager={FIELD_MANAGER}", "-f", "-"], manifest) if proc.returncode != 0: return { "ok": False, "action": "apply-failed", "stdoutTail": text(proc.stdout, 2000), "stderrTail": text(proc.stderr, 4000), "valuesPrinted": False, } status = sentinel_runtime_status() return { "ok": status.get("ok") is True, "action": "applied", "stdoutTail": text(proc.stdout, 2000), "runtime": status, "valuesPrinted": False, } def safe_kube_json(args, label): proc = kubectl([*args, "-o", "json"]) if proc.returncode != 0: return None, {"label": label, "exitCode": proc.returncode, "stderrTail": text(proc.stderr, 1000)} try: return json.loads(proc.stdout.decode("utf-8")), None except Exception as exc: return None, {"label": label, "exitCode": proc.returncode, "error": str(exc), "stdoutTail": text(proc.stdout, 1000)} def sentinel_runtime_status(): if not TARGET_SENTINEL_ENABLED: return { "ok": True, "action": "skipped-target-disabled", "desired": { "monitorEnabled": SENTINEL_CONFIG.get("monitor", {}).get("enabled"), "actionsEnabled": SENTINEL_CONFIG.get("actions", {}).get("enabled"), }, "valuesPrinted": False, } cfg = SENTINEL_CONFIG cronjob_name = cfg.get("cronJobName") secret_name = cfg.get("credentialsSecretName") configmap_name = cfg.get("configMapName") state_name = cfg.get("stateConfigMapName") cronjob, cronjob_error = safe_kube_json(["-n", NAMESPACE, "get", "cronjob", cronjob_name], f"cronjob/{cronjob_name}") secret, secret_error = safe_kube_json(["-n", NAMESPACE, "get", "secret", secret_name], f"secret/{secret_name}") configmap, configmap_error = safe_kube_json(["-n", NAMESPACE, "get", "configmap", configmap_name], f"configmap/{configmap_name}") state_cm, state_error = safe_kube_json(["-n", NAMESPACE, "get", "configmap", state_name], f"configmap/{state_name}") state = None if isinstance(state_cm, dict): raw_state = (state_cm.get("data") or {}).get("state.json") if isinstance(raw_state, str) and raw_state: try: state = json.loads(raw_state) except Exception as exc: state = {"parseError": str(exc)} accounts = (state.get("accounts") or {}) if isinstance(state, dict) else {} quarantined = [] recent_accounts = [] for name, account_state in accounts.items(): if not isinstance(account_state, dict): continue quarantine = account_state.get("quarantine") if isinstance(quarantine, dict) and quarantine.get("active") is True: quarantined.append({ "accountName": name, "until": quarantine.get("until"), "applied": quarantine.get("applied"), "reason": quarantine.get("reason"), "failureKind": quarantine.get("failureKind"), "errorDetails": quarantine.get("errorDetails"), "intervalMinutes": quarantine.get("intervalMinutes"), "sentinelProtect": quarantine.get("sentinelProtect"), }) last_probe = account_state.get("lastProbe") if isinstance(last_probe, dict): recent_accounts.append({ "accountName": name, "lastProbeAt": account_state.get("lastProbeAt"), "lastStatus": account_state.get("lastStatus"), "nextProbeAfter": account_state.get("nextProbeAfter"), "ok": last_probe.get("ok"), "purpose": last_probe.get("purpose"), "httpStatus": last_probe.get("httpStatus"), "durationMs": last_probe.get("durationMs"), "markerMatched": last_probe.get("markerMatched"), "sentinelProtect": last_probe.get("sentinelProtect") or account_state.get("sentinelProtectConfig"), "outputHash": last_probe.get("outputHash"), "outputPreview": last_probe.get("outputPreview"), "responseBodyHash": last_probe.get("responseBodyHash"), "responseBodyPreview": last_probe.get("responseBodyPreview"), "error": last_probe.get("error"), "errorDetails": last_probe.get("errorDetails"), "usage": last_probe.get("usage"), "failureKind": last_probe.get("failureKind"), "requestShape": last_probe.get("requestShape"), "action": last_probe.get("action"), }) recent_accounts.sort(key=lambda item: item.get("lastProbeAt") or "") last_run = state.get("lastRun") if isinstance(state, dict) else None cronjob_spec = cronjob.get("spec") if isinstance(cronjob, dict) else {} secret_data = secret.get("data") if isinstance(secret, dict) else {} configmap_data = configmap.get("data") if isinstance(configmap, dict) else {} ok = cronjob is not None and secret is not None and configmap is not None return { "ok": ok, "desired": { "monitorEnabled": cfg.get("monitor", {}).get("enabled"), "actionsEnabled": cfg.get("actions", {}).get("enabled"), "schedule": cfg.get("schedule"), "cronJobName": cronjob_name, "configMapName": configmap_name, "credentialsSecretName": secret_name, "stateConfigMapName": state_name, }, "cronJob": { "exists": cronjob is not None, "schedule": cronjob_spec.get("schedule") if isinstance(cronjob_spec, dict) else None, "suspend": cronjob_spec.get("suspend") if isinstance(cronjob_spec, dict) else None, "lastScheduleTime": (cronjob.get("status") or {}).get("lastScheduleTime") if isinstance(cronjob, dict) else None, "active": len((cronjob.get("status") or {}).get("active") or []) if isinstance(cronjob, dict) else None, "error": cronjob_error, }, "secret": { "exists": secret is not None, "profileSecretPresent": isinstance(secret_data, dict) and "profiles.json" in secret_data, "valuesPrinted": False, "error": secret_error, }, "configMap": { "exists": configmap is not None, "configPresent": isinstance(configmap_data, dict) and "config.json" in configmap_data, "runnerPresent": isinstance(configmap_data, dict) and "sentinel.py" in configmap_data, "error": configmap_error, }, "state": { "exists": state_cm is not None, "accountCount": len(accounts), "quarantinedCount": len(quarantined), "quarantined": quarantined[-10:], "recentAccounts": recent_accounts[-12:], "lastRun": last_run, "error": state_error, }, "valuesPrinted": False, } def parse_epoch_z(value): if not isinstance(value, str) or not value: return None try: return datetime.fromisoformat(value.replace("Z", "+00:00")).timestamp() except Exception: return None def sentinel_state_object(): if not TARGET_SENTINEL_ENABLED: return None, None state_name = SENTINEL_CONFIG.get("stateConfigMapName") if not state_name: return None, None obj, err = safe_kube_json(["-n", NAMESPACE, "get", "configmap", state_name], f"configmap/{state_name}") if not isinstance(obj, dict): return None, None raw_state = (obj.get("data") or {}).get("state.json") if not isinstance(raw_state, str) or not raw_state: return obj, None try: return obj, json.loads(raw_state) except Exception: return obj, None def active_sentinel_quarantine_names(): if not TARGET_SENTINEL_ENABLED: return set() _, state = sentinel_state_object() if not isinstance(state, dict): return set() accounts_state = state.get("accounts") if isinstance(state.get("accounts"), dict) else {} names = set() for name, account_state in accounts_state.items(): if not isinstance(name, str) or not isinstance(account_state, dict): continue quarantine = account_state.get("quarantine") if isinstance(quarantine, dict) and quarantine.get("active") is True and quarantine.get("applied") is True: names.add(name) return names def default_sentinel_state(): return {"version": 1, "accounts": {}, "ledger": {}, "history": []} def clamp_sentinel_freezes_for_config(state, now): freeze_config = SENTINEL_CONFIG.get("freeze") if isinstance(SENTINEL_CONFIG.get("freeze"), dict) else {} try: max_interval = int(freeze_config.get("maxTtlMinutes") or 10) except Exception: max_interval = 10 accounts_state = state.get("accounts") if isinstance(state.get("accounts"), dict) else {} now_epoch = time.time() items = [] for name, account_state in accounts_state.items(): if not isinstance(name, str) or not isinstance(account_state, dict): continue quarantine = account_state.get("quarantine") if not isinstance(quarantine, dict) or quarantine.get("active") is not True or quarantine.get("applied") is not True: continue try: interval = int(quarantine.get("intervalMinutes") or 0) except Exception: interval = 0 until_epoch = parse_epoch_z(quarantine.get("until")) old_until = quarantine.get("until") if interval <= max_interval and (until_epoch is None or until_epoch <= now_epoch + max_interval * 60): continue quarantine["previousIntervalMinutes"] = interval quarantine["intervalMinutes"] = max_interval quarantine["until"] = now quarantine["clampedAt"] = now quarantine["clampedBy"] = "sync-freeze-max-ttl" account_state["nextProbeAfter"] = now items.append({ "accountName": name, "previousIntervalMinutes": interval, "maxIntervalMinutes": max_interval, "previousUntil": old_until, "nextProbeAfter": now, }) return items def parse_iso_epoch(value): if not isinstance(value, str) or not value: return None try: return datetime.fromisoformat(value.replace("Z", "+00:00")).timestamp() except Exception: return None def profile_success_max_interval(profile): cadence = SENTINEL_CONFIG.get("cadence") if isinstance(SENTINEL_CONFIG.get("cadence"), dict) else {} legacy = cadence.get("successMaxIntervalMinutes") if legacy is None: legacy = cadence.get("trustedSuccessMaxIntervalMinutes") or cadence.get("untrustedSuccessMaxIntervalMinutes") or 1 if profile.get("trustUpstream") is True: value = cadence.get("trustedSuccessMaxIntervalMinutes") or legacy else: value = cadence.get("untrustedSuccessMaxIntervalMinutes") or legacy try: return int(value) except Exception: return int(legacy) def clamp_sentinel_success_cadence_for_config(state, profiles, now): accounts_state = state.get("accounts") if isinstance(state.get("accounts"), dict) else {} profile_map = {item.get("accountName"): item for item in profiles if isinstance(item, dict) and isinstance(item.get("accountName"), str)} now_epoch = time.time() items = [] for name, profile in profile_map.items(): account_state = accounts_state.get(name) if not isinstance(account_state, dict): continue quarantine = account_state.get("quarantine") if isinstance(quarantine, dict) and quarantine.get("active") is True: account_state["trustUpstream"] = profile.get("trustUpstream") is True account_state["sentinelProtectConfig"] = profile.get("sentinelProtect") if isinstance(profile.get("sentinelProtect"), dict) else {"enabled": False} account_state["successMaxIntervalMinutes"] = profile_success_max_interval(profile) continue try: interval = int(account_state.get("successIntervalMinutes") or 0) except Exception: interval = 0 next_epoch = parse_iso_epoch(account_state.get("nextProbeAfter")) max_interval = profile_success_max_interval(profile) account_state["trustUpstream"] = profile.get("trustUpstream") is True account_state["sentinelProtectConfig"] = profile.get("sentinelProtect") if isinstance(profile.get("sentinelProtect"), dict) else {"enabled": False} account_state["successMaxIntervalMinutes"] = max_interval if interval <= max_interval and (next_epoch is None or next_epoch <= now_epoch + max_interval * 60): continue old_next = account_state.get("nextProbeAfter") account_state["previousSuccessIntervalMinutes"] = interval account_state["successIntervalMinutes"] = min(interval, max_interval) if interval > 0 else interval account_state["nextProbeAfter"] = now account_state["cadenceClampedAt"] = now account_state["cadenceClampedBy"] = "sync-success-max-interval" items.append({ "accountName": name, "trustUpstream": profile.get("trustUpstream") is True, "previousSuccessIntervalMinutes": interval, "maxIntervalMinutes": max_interval, "previousNextProbeAfter": old_next, "nextProbeAfter": now, }) return items def update_sentinel_state_configmap(obj, state): state_name = SENTINEL_CONFIG.get("stateConfigMapName") if not state_name: return {"ok": False, "reason": "state-configmap-missing"} state_json = json.dumps(state, ensure_ascii=False, indent=2) manifest = { "apiVersion": "v1", "kind": "ConfigMap", "metadata": { "name": state_name, "namespace": NAMESPACE, "labels": { "app.kubernetes.io/name": SERVICE_NAME, "app.kubernetes.io/component": "account-sentinel", "app.kubernetes.io/managed-by": "unidesk-platform-infra", }, }, "data": {"state.json": state_json}, } proc = kubectl(["apply", "--server-side", "--force-conflicts", f"--field-manager={FIELD_MANAGER}", "-f", "-"], json.dumps(manifest)) action = "applied" if isinstance(obj, dict) else "created" if proc.returncode != 0: return {"ok": False, "reason": f"{action}-failed", "stderrTail": text(proc.stderr, 2000)} return {"ok": True, "action": action} def ensure_sentinel_state_for_sync(account_results, pending_only=False): if not sentinel_quality_gate_enabled(): return {"ok": True, "skipped": True, "reason": "sentinel-quality-gate-disabled", "changedCount": 0, "items": [], "valuesPrinted": False} state_obj, state = sentinel_state_object() if not isinstance(state, dict): state = default_sentinel_state() state.setdefault("version", 1) accounts_state = state.setdefault("accounts", {}) if not isinstance(accounts_state, dict): accounts_state = {} state["accounts"] = accounts_state now = utc_iso() items = [] clamped_items = [] if pending_only else clamp_sentinel_freezes_for_config(state, now) cadence_clamped_items = [] if pending_only else clamp_sentinel_success_cadence_for_config(state, [item.get("profileConfig") for item in account_results if isinstance(item.get("profileConfig"), dict)], now) changed_count = 0 fingerprint_only_count = 0 for item in account_results: name = item.get("accountName") if not isinstance(name, str) or not name: continue account_state = accounts_state.setdefault(name, {}) if not isinstance(account_state, dict): account_state = {} accounts_state[name] = account_state fingerprint_value = item.get("sentinelProbeConfigFingerprint") if isinstance(fingerprint_value, str) and fingerprint_value: account_state["probeConfigFingerprint"] = fingerprint_value if item.get("sentinelProbeRequired") is not True: fingerprint_only_count += 1 continue changed_count += 1 reasons = item.get("sentinelChangeReasons") if isinstance(item.get("sentinelChangeReasons"), list) else [] quarantine = account_state.get("quarantine") if isinstance(account_state.get("quarantine"), dict) else None if not (isinstance(quarantine, dict) and quarantine.get("active") is True): account_state["quarantine"] = { "active": False, "reason": "yaml-account-change-pending-sentinel-probe", "lastPendingAt": now, "changeReasons": reasons, } account_state["nextProbeAfter"] = now account_state["successStreak"] = 0 account_state["successIntervalMinutes"] = 0 profile_config = item.get("profileConfig") if isinstance(item.get("profileConfig"), dict) else {} account_state["trustUpstream"] = profile_config.get("trustUpstream") is True account_state["sentinelProtectConfig"] = profile_config.get("sentinelProtect") if isinstance(profile_config.get("sentinelProtect"), dict) else {"enabled": False} account_state["successMaxIntervalMinutes"] = profile_success_max_interval(profile_config) account_state["lastStatus"] = "pending-sentinel-quality-gate" account_state["qualityGate"] = { "pending": True, "reason": "yaml-account-change", "changeReasons": reasons, "markedAt": now, "pendingOnly": pending_only, "defaultFrozen": False, } items.append({"accountName": name, "changeReasons": reasons, "nextProbeAfter": now, "defaultFrozen": False, "defaultSchedulable": True, "pendingOnly": pending_only}) if changed_count <= 0 and len(clamped_items) <= 0 and len(cadence_clamped_items) <= 0: return {"ok": True, "skipped": False, "reason": "no-new-or-changed-accounts", "changedCount": 0, "fingerprintOnlyCount": fingerprint_only_count, "clampedCount": 0, "cadenceClampedCount": 0, "items": [], "valuesPrinted": False} update = update_sentinel_state_configmap(state_obj, state) if pending_only and changed_count > 0: reason = "new-or-changed-accounts-pending-probe-prepared-default-schedulable" elif changed_count > 0 and (len(clamped_items) > 0 or len(cadence_clamped_items) > 0): reason = "new-or-changed-accounts-default-schedulable-and-sentinel-cadence-clamped" elif changed_count > 0: reason = "new-or-changed-accounts-default-schedulable" elif len(cadence_clamped_items) > 0: reason = "success-cadence-clamped-to-current-config" else: reason = "freeze-backoff-clamped-to-current-config" return { "ok": update.get("ok") is True, "skipped": False, "reason": reason, "changedCount": changed_count, "fingerprintOnlyCount": fingerprint_only_count, "clampedCount": len(clamped_items), "cadenceClampedCount": len(cadence_clamped_items), "pendingOnly": pending_only, "items": items, "clampedItems": clamped_items, "cadenceClampedItems": cadence_clamped_items, "update": update, "valuesPrinted": False, } def sentinel_state_summary(): _, state = sentinel_state_object() if not isinstance(state, dict): return {"exists": False, "valuesPrinted": False} accounts = state.get("accounts") if isinstance(state.get("accounts"), dict) else {} quarantined = [] recent_accounts = [] for name, account_state in accounts.items(): if not isinstance(account_state, dict): continue quarantine = account_state.get("quarantine") if isinstance(quarantine, dict) and quarantine.get("active") is True: quarantined.append({ "accountName": name, "until": quarantine.get("until"), "applied": quarantine.get("applied"), "reason": quarantine.get("reason"), "failureKind": quarantine.get("failureKind"), "errorDetails": quarantine.get("errorDetails"), "intervalMinutes": quarantine.get("intervalMinutes"), "sentinelProtect": quarantine.get("sentinelProtect"), }) last_probe = account_state.get("lastProbe") if isinstance(last_probe, dict): recent_accounts.append({ "accountName": name, "lastProbeAt": account_state.get("lastProbeAt"), "lastStatus": account_state.get("lastStatus"), "nextProbeAfter": account_state.get("nextProbeAfter"), "ok": last_probe.get("ok"), "purpose": last_probe.get("purpose"), "httpStatus": last_probe.get("httpStatus"), "durationMs": last_probe.get("durationMs"), "markerMatched": last_probe.get("markerMatched"), "sentinelProtect": last_probe.get("sentinelProtect") or account_state.get("sentinelProtectConfig"), "usage": last_probe.get("usage"), "responseBodyHash": last_probe.get("responseBodyHash"), "responseBodyPreview": last_probe.get("responseBodyPreview"), "error": last_probe.get("error"), "errorDetails": last_probe.get("errorDetails"), "failureKind": last_probe.get("failureKind"), "requestShape": last_probe.get("requestShape"), "action": last_probe.get("action"), }) recent_accounts.sort(key=lambda item: item.get("lastProbeAt") or "") return { "exists": True, "accountCount": len(accounts), "quarantinedCount": len(quarantined), "quarantined": quarantined[-10:], "recentAccounts": recent_accounts[-12:], "lastRun": state.get("lastRun"), "valuesPrinted": False, } def reassert_sentinel_freezes_after_sync(token): if not TARGET_SENTINEL_ENABLED: return {"ok": True, "skipped": True, "reason": "target-disabled", "items": [], "valuesPrinted": False} if (SENTINEL_CONFIG.get("actions") or {}).get("enabled") is not True: return {"ok": True, "skipped": True, "reason": "actions-disabled", "items": [], "valuesPrinted": False} _, state = sentinel_state_object() if not isinstance(state, dict): return {"ok": True, "skipped": True, "reason": "state-missing", "items": [], "valuesPrinted": False} accounts_state = state.get("accounts") if isinstance(state.get("accounts"), dict) else {} accounts = list_accounts(token) by_name = {item.get("name"): item for item in accounts if isinstance(item.get("name"), str)} items = [] for name, account_state in accounts_state.items(): if not isinstance(account_state, dict): continue quarantine = account_state.get("quarantine") if not isinstance(quarantine, dict) or quarantine.get("active") is not True or quarantine.get("applied") is not True: continue account = by_name.get(name) if not account or account.get("id") is None: items.append({"accountName": name, "ok": False, "reason": "account-not-found"}) continue try: ensure_success( curl_api("POST", f"/api/v1/admin/accounts/{account['id']}/schedulable", bearer=token, payload={"schedulable": False}), f"reassert sentinel freeze for {name}", ) items.append({"accountName": name, "accountId": account.get("id"), "ok": True, "until": quarantine.get("until")}) except Exception as exc: items.append({"accountName": name, "accountId": account.get("id"), "ok": False, "error": str(exc)}) return { "ok": all(item.get("ok") is True for item in items), "skipped": False, "items": items, "valuesPrinted": False, } def list_user_keys(token): data = ensure_success(curl_api("GET", "/api/v1/keys?page=1&page_size=200", bearer=token), "list user keys") return extract_items(data) def ensure_sub2api_api_key(token, api_key, group_id): keys = list_user_keys(token) existing = next((item for item in keys if item.get("key") == api_key), None) action = "kept-existing" if existing is None: existing = next((item for item in keys if item.get("name") == POOL_API_KEY_NAME), None) if existing is None or existing.get("key") != api_key: payload = { "name": POOL_API_KEY_NAME, "group_id": group_id, "custom_key": api_key, "quota": 0, "rate_limit_5h": 0, "rate_limit_1d": 0, "rate_limit_7d": 0, } created = ensure_success(curl_api("POST", "/api/v1/keys", bearer=token, payload=payload), "create pool API key") existing = created if isinstance(created, dict) else existing action = "created" elif existing.get("id") is not None and existing.get("group_id") != group_id: updated = ensure_success(curl_api("PUT", f"/api/v1/keys/{existing['id']}", bearer=token, payload={"name": POOL_API_KEY_NAME, "group_id": group_id}), "update pool API key group") existing = updated if isinstance(updated, dict) else existing action = "updated-group" return { "action": action, "id": existing.get("id") if isinstance(existing, dict) else None, "name": existing.get("name") if isinstance(existing, dict) else POOL_API_KEY_NAME, "groupId": existing.get("group_id") if isinstance(existing, dict) else group_id, "userId": existing.get("user_id") if isinstance(existing, dict) else None, } def get_admin_user(token, user_id): data = ensure_success(curl_api("GET", f"/api/v1/admin/users/{user_id}", bearer=token), "get API key owner") if not isinstance(data, dict): raise RuntimeError("API key owner response is not an object") return data def ensure_pool_owner_balance(token, user_id): user = get_admin_user(token, user_id) current = float(user.get("balance") or 0) if current >= MIN_OWNER_BALANCE_USD: return { "action": "kept-existing", "userId": user_id, "balanceBefore": current, "balanceAfter": current, "minimumBalanceUsd": MIN_OWNER_BALANCE_USD, } updated = ensure_success(curl_api("POST", f"/api/v1/admin/users/{user_id}/balance", bearer=token, payload={ "balance": MIN_OWNER_BALANCE_USD, "operation": "set", "notes": "UniDesk Sub2API Codex pool internal API key bootstrap balance.", }), "set API key owner balance") after = float(updated.get("balance") or MIN_OWNER_BALANCE_USD) if isinstance(updated, dict) else MIN_OWNER_BALANCE_USD return { "action": "set", "userId": user_id, "balanceBefore": current, "balanceAfter": after, "minimumBalanceUsd": MIN_OWNER_BALANCE_USD, } def ensure_pool_owner_concurrency(token, user_id): user = get_admin_user(token, user_id) try: current = int(user.get("concurrency") or 0) except Exception: current = 0 if current >= MIN_OWNER_CONCURRENCY: return { "ok": True, "action": "kept-existing", "userId": user_id, "concurrencyBefore": current, "concurrencyAfter": current, "minimumConcurrency": MIN_OWNER_CONCURRENCY, "minimumConcurrencySource": MIN_OWNER_CONCURRENCY_SOURCE, } updated = ensure_success(curl_api("PUT", f"/api/v1/admin/users/{user_id}", bearer=token, payload={ "concurrency": MIN_OWNER_CONCURRENCY, }), "set API key owner concurrency") try: after = int(updated.get("concurrency") or MIN_OWNER_CONCURRENCY) if isinstance(updated, dict) else MIN_OWNER_CONCURRENCY except Exception: after = MIN_OWNER_CONCURRENCY return { "ok": after >= MIN_OWNER_CONCURRENCY, "action": "set", "userId": user_id, "concurrencyBefore": current, "concurrencyAfter": after, "minimumConcurrency": MIN_OWNER_CONCURRENCY, "minimumConcurrencySource": MIN_OWNER_CONCURRENCY_SOURCE, } def validate_gateway(api_key): resp = curl_api("GET", "/v1/models", bearer=api_key) parsed = resp.get("json") model_count = None if isinstance(parsed, dict) and isinstance(parsed.get("data"), list): model_count = len(parsed["data"]) return { "ok": resp.get("ok"), "httpStatus": resp.get("httpStatus"), "transportExitCode": resp.get("transportExitCode"), "modelCount": model_count, "bodyPreview": text(resp.get("body", ""), 500) if not resp.get("ok") else "", "stderr": resp.get("stderr", ""), "method": "GET /v1/models", "serviceDns": SERVICE_DNS, "valuesPrinted": False, } def response_output_preview(parsed): if not isinstance(parsed, dict): return "" if isinstance(parsed.get("output_text"), str): return parsed["output_text"][:240] output = parsed.get("output") if not isinstance(output, list): return "" parts = [] for item in output: if not isinstance(item, dict): continue content = item.get("content") if not isinstance(content, list): continue for block in content: if not isinstance(block, dict): continue text_value = block.get("text") if isinstance(text_value, str) and text_value: parts.append(text_value) return "\\n".join(parts)[:240] def request_log_evidence(request_id): proc = runtime_logs("5m", 800) stdout = proc.stdout.decode("utf-8", errors="replace") lines = [line for line in stdout.splitlines() if request_id in line] failovers = [] final = None for line in lines: json_start = line.find("{") if json_start < 0: continue try: item = json.loads(line[json_start:]) except Exception: continue if "upstream_failover_switching" in line: failovers.append({ "accountId": item.get("account_id"), "upstreamStatus": item.get("upstream_status"), "switchCount": item.get("switch_count"), "maxSwitches": item.get("max_switches"), }) if "http request completed" in line: final = { "accountId": item.get("account_id"), "statusCode": item.get("status_code"), "latencyMs": item.get("latency_ms"), "path": item.get("path"), } return { "requestId": request_id, "matchedLogLineCount": len(lines), "failovers": failovers, "final": final, "logsExitCode": proc.returncode, "logsStderr": text(proc.stderr, 1000), } def recent_compact_gateway_evidence(): proc = runtime_logs("6h", 2500) stdout = proc.stdout.decode("utf-8", errors="replace") failures = [] successes = [] failovers = [] final_errors = [] context_canceled = [] for line in stdout.splitlines(): if "/responses/compact" not in line and "remote_compact" not in line: continue json_start = line.find("{") if json_start < 0: continue try: item = json.loads(line[json_start:]) except Exception: continue path = item.get("path") entry = { "requestId": item.get("request_id"), "clientRequestId": item.get("client_request_id"), "accountId": item.get("account_id"), "statusCode": item.get("status_code"), "upstreamStatus": item.get("upstream_status"), "latencyMs": item.get("latency_ms"), "path": path, } if "codex.remote_compact.failed" in line: failures.append(entry) elif "codex.remote_compact.succeeded" in line: successes.append(entry) elif "upstream_failover_switching" in line and path == "/responses/compact": failovers.append({ **entry, "switchCount": item.get("switch_count"), "maxSwitches": item.get("max_switches"), }) elif "http request completed" in line and path == "/responses/compact" and isinstance(item.get("status_code"), int) and item.get("status_code") >= 400: final_errors.append(entry) if "context canceled" in line and path == "/responses/compact": context_canceled.append(entry) return { "ok": True, "degraded": len(failures) > 0 or len(final_errors) > 0 or len(context_canceled) > 0, "window": "6h", "tailLines": 2500, "failureCount": len(failures), "successCount": len(successes), "failoverCount": len(failovers), "finalErrorCount": len(final_errors), "contextCanceledCount": len(context_canceled), "recentFailures": failures[-5:], "recentSuccesses": successes[-5:], "recentFailovers": failovers[-8:], "recentFinalErrors": final_errors[-5:], "recentContextCanceled": context_canceled[-5:], "logsExitCode": proc.returncode, "logsStderr": text(proc.stderr, 1000), "valuesPrinted": False, } def is_ignored_probe_noise(entry): for value in (entry.get("requestId"), entry.get("clientRequestId")): if isinstance(value, str) and value.startswith("unidesk-400-model-probe-"): return True return False def filter_ignored_probe_noise(items): return [item for item in items if not is_ignored_probe_noise(item)] def ignored_probe_noise(items, section): result = [] for item in items: if is_ignored_probe_noise(item): probe = dict(item) probe["section"] = section result.append(probe) return result def group_by_request_id(items): grouped = {} for item in items: request_id = item.get("requestId") if not isinstance(request_id, str) or not request_id: continue grouped.setdefault(request_id, []).append(item) return grouped def failover_budget_exhausted_evidence(failovers, final_errors): final_by_request = {} for item in final_errors: request_id = item.get("requestId") if isinstance(request_id, str) and request_id: final_by_request[request_id] = item exhausted = [] for request_id, request_failovers in group_by_request_id(failovers).items(): final = final_by_request.get(request_id) if not final: continue last = request_failovers[-1] switch_count = last.get("switchCount") max_switches = last.get("maxSwitches") final_status = final.get("statusCode") if ( isinstance(switch_count, int) and isinstance(max_switches, int) and max_switches > 0 and switch_count >= max_switches and isinstance(final_status, int) and final_status >= 500 ): exhausted.append({ "requestId": request_id, "clientRequestId": final.get("clientRequestId") or last.get("clientRequestId"), "path": final.get("path") or last.get("path"), "finalAccountId": final.get("accountId"), "finalStatusCode": final_status, "switchCount": switch_count, "maxSwitches": max_switches, "lastFailoverAccountId": last.get("accountId"), "lastUpstreamStatus": last.get("upstreamStatus"), }) return exhausted def recent_responses_gateway_evidence(): proc = runtime_logs("6h", 2500) stdout = proc.stdout.decode("utf-8", errors="replace") failovers = [] forward_failures = [] final_errors = [] context_canceled = [] slow_final_errors = [] for line in stdout.splitlines(): if '"/responses"' not in line and '"/v1/responses"' not in line: continue json_start = line.find("{") if json_start < 0: continue try: item = json.loads(line[json_start:]) except Exception: continue path = item.get("path") if path not in ("/responses", "/v1/responses"): continue entry = { "requestId": item.get("request_id"), "clientRequestId": item.get("client_request_id"), "accountId": item.get("account_id"), "statusCode": item.get("status_code"), "upstreamStatus": item.get("upstream_status"), "latencyMs": item.get("latency_ms"), "path": path, } if "upstream_failover_switching" in line: failovers.append({ **entry, "switchCount": item.get("switch_count"), "maxSwitches": item.get("max_switches"), }) elif "openai.forward_failed" in line: forward_failures.append({ **entry, "errorPreview": text(str(item.get("error") or ""), 500), "fallbackErrorResponseWritten": item.get("fallback_error_response_written"), "upstreamErrorResponseAlreadyWritten": item.get("upstream_error_response_already_written"), }) elif "http request completed" in line and isinstance(item.get("status_code"), int) and item.get("status_code") >= 400: final_errors.append(entry) latency_ms = item.get("latency_ms") if isinstance(latency_ms, int) and latency_ms >= 30000: slow_final_errors.append(entry) if "context canceled" in line: context_canceled.append(entry) visible_failovers = filter_ignored_probe_noise(failovers) visible_forward_failures = filter_ignored_probe_noise(forward_failures) visible_final_errors = filter_ignored_probe_noise(final_errors) visible_slow_final_errors = filter_ignored_probe_noise(slow_final_errors) visible_context_canceled = filter_ignored_probe_noise(context_canceled) failover_budget_exhausted = failover_budget_exhausted_evidence(visible_failovers, visible_final_errors) probe_noise = ( ignored_probe_noise(failovers, "failovers") + ignored_probe_noise(forward_failures, "forwardFailures") + ignored_probe_noise(final_errors, "finalErrors") + ignored_probe_noise(slow_final_errors, "slowFinalErrors") + ignored_probe_noise(context_canceled, "contextCanceled") ) return { "ok": True, "degraded": len(visible_forward_failures) > 0 or len(visible_final_errors) > 0 or len(visible_context_canceled) > 0, "window": "6h", "tailLines": 2500, "failoverCount": len(visible_failovers), "forwardFailureCount": len(visible_forward_failures), "finalErrorCount": len(visible_final_errors), "slowFinalErrorCount": len(visible_slow_final_errors), "contextCanceledCount": len(visible_context_canceled), "ignoredProbeNoiseCount": len(probe_noise), "failoverBudgetExhausted": failover_budget_exhausted[-8:], "rawCounts": { "failoverCount": len(failovers), "forwardFailureCount": len(forward_failures), "finalErrorCount": len(final_errors), "slowFinalErrorCount": len(slow_final_errors), "contextCanceledCount": len(context_canceled), }, "recentFailovers": visible_failovers[-8:], "recentForwardFailures": visible_forward_failures[-8:], "recentFinalErrors": visible_final_errors[-8:], "recentSlowFinalErrors": visible_slow_final_errors[-5:], "recentContextCanceled": visible_context_canceled[-5:], "recentProbeNoise": probe_noise[-5:], "logsExitCode": proc.returncode, "logsStderr": text(proc.stderr, 1000), "valuesPrinted": False, } def validate_gateway_responses(api_key): request_id = "unidesk-codex-pool-validate-" + str(int(time.time() * 1000)) payload = { "model": RESPONSES_SMOKE_MODEL, "input": "Reply exactly: unidesk-sub2api-validate-ok", "stream": False, "store": False, "max_output_tokens": 32, } body = json.dumps(payload, separators=(",", ":")).encode("utf-8") script = r''' set -eu token="$1" request_id="$2" url="$3" tmp="$(mktemp)" trap 'rm -f "$tmp"' EXIT cat > "$tmp" curl -sS -w '\\n__HTTP_CODE__:%{http_code}' -X POST \ -H "Authorization: Bearer $token" \ -H 'Content-Type: application/json' \ -H "X-Request-ID: $request_id" \ -H "OpenAI-Client-Request-ID: $request_id" \ --data-binary @"$tmp" \ "$url" ''' started = time.time() if RUNTIME_MODE == "host-docker": if not isinstance(HOST_DOCKER_APP_PORT, int): raise RuntimeError("host-docker app port missing") proc = run(["sh", "-c", script, "sh", api_key, request_id, f"http://127.0.0.1:{HOST_DOCKER_APP_PORT}/v1/responses"], body) else: proc = run([ "kubectl", "-n", NAMESPACE, "exec", "-i", APP_POD, "--", "sh", "-c", script, "sh", api_key, request_id, "http://127.0.0.1:8080/v1/responses", ], body) resp = parse_curl_output(proc) evidence = request_log_evidence(request_id) parsed = resp.get("json") failover_count = len(evidence.get("failovers") or []) return { "ok": resp.get("ok"), "degraded": failover_count > 0, "outcome": "succeeded-with-failover" if resp.get("ok") and failover_count > 0 else ("succeeded" if resp.get("ok") else "failed"), "httpStatus": resp.get("httpStatus"), "transportExitCode": resp.get("transportExitCode"), "method": "POST /v1/responses", "model": RESPONSES_SMOKE_MODEL, "requestId": request_id, "durationMs": int((time.time() - started) * 1000), "outputTextPreview": response_output_preview(parsed), "bodyPreview": "" if resp.get("ok") else text(resp.get("body", ""), 800), "stderr": resp.get("stderr", ""), "evidence": evidence, "valuesPrinted": False, } def bool_value(value): if isinstance(value, bool): return value if isinstance(value, str): if value.lower() == "true": return True if value.lower() == "false": return False return False def normalize_temp_unschedulable_credentials(credentials): if not isinstance(credentials, dict): credentials = {} enabled = bool_value(credentials.get("temp_unschedulable_enabled")) raw_rules = credentials.get("temp_unschedulable_rules") if isinstance(raw_rules, str): try: raw_rules = json.loads(raw_rules) except json.JSONDecodeError: raw_rules = [] rules = [] if isinstance(raw_rules, list): for rule in raw_rules: if not isinstance(rule, dict): continue error_code = rule.get("error_code", rule.get("statusCode")) duration_minutes = rule.get("duration_minutes", rule.get("durationMinutes")) keywords = rule.get("keywords") if not isinstance(error_code, int) or not isinstance(duration_minutes, int) or not isinstance(keywords, list): continue clean_keywords = [item for item in keywords if isinstance(item, str) and item.strip()] if not clean_keywords: continue description = rule.get("description") if isinstance(rule.get("description"), str) else "" rules.append({ "error_code": error_code, "keywords": clean_keywords, "duration_minutes": duration_minutes, "description": description, }) return { "enabled": enabled, "rules": rules, } def summarize_temp_unschedulable_rules(rules): return [{ "errorCode": rule.get("error_code"), "durationMinutes": rule.get("duration_minutes"), "keywordCount": len(rule.get("keywords") or []), "keywords": rule.get("keywords") or [], "hasDescription": bool(rule.get("description")), } for rule in rules] def success_body_reclassification_requirement(): for name in sorted(EXPECTED_ACCOUNT_TEMP_UNSCHEDULABLE): expected = normalize_temp_unschedulable_credentials(EXPECTED_ACCOUNT_TEMP_UNSCHEDULABLE[name]) if expected["enabled"] is not True: continue for rule in expected["rules"]: error_code = rule.get("error_code") keywords = rule.get("keywords") or [] if isinstance(error_code, int) and 200 <= error_code < 300 and keywords: return { "required": True, "sourceAccountName": name, "statusCode": error_code, "keywords": keywords, "representativeKeyword": keywords[0], "durationMinutes": rule.get("duration_minutes"), } return { "required": False, "sourceAccountName": None, "statusCode": None, "keywords": [], "representativeKeyword": None, "durationMinutes": None, } def model_routing_400_failover_requirement(): preferred = ["invalid_encrypted_content", "encrypted content", "could not be verified", "could not be decrypted", "暂不支持", "可用模型", "unsupported model", "model not supported", "does not support", "not supported", "model_not_found", "no available channel for model"] for name in sorted(EXPECTED_ACCOUNT_TEMP_UNSCHEDULABLE): expected = normalize_temp_unschedulable_credentials(EXPECTED_ACCOUNT_TEMP_UNSCHEDULABLE[name]) if expected["enabled"] is not True: continue for rule in expected["rules"]: error_code = rule.get("error_code") keywords = rule.get("keywords") or [] if error_code != 400 or not keywords: continue representative_keyword = next((item for item in preferred if item in keywords), keywords[0]) return { "required": True, "sourceAccountName": name, "statusCode": error_code, "keywords": keywords, "representativeKeyword": representative_keyword, "durationMinutes": rule.get("duration_minutes"), } return { "required": False, "sourceAccountName": None, "statusCode": None, "keywords": [], "representativeKeyword": None, "durationMinutes": None, } def delete_probe_resource(token, method, path, label): if not path: return {"label": label, "ok": True, "skipped": True} resp = curl_api(method, path, bearer=token) ok = resp.get("ok") is True or resp.get("httpStatus") in (404, 410) return { "label": label, "ok": ok, "method": method, "path": path, "httpStatus": resp.get("httpStatus"), "transportExitCode": resp.get("transportExitCode"), "bodyPreview": "" if ok else text(resp.get("body", ""), 500), "valuesPrinted": False, } def validate_runtime_capabilities(token): success_body = success_body_reclassification_requirement() model_routing_400 = model_routing_400_failover_requirement() return { "ok": success_body.get("required") is not True and model_routing_400.get("required") is True, "runtimeImage": app_pod_runtime_image(), "successBodyReclassification": { "ok": success_body.get("required") is not True, "required": success_body.get("required"), "outcome": "not-required-by-yaml" if success_body.get("required") is not True else "requires-source-review-and-real-traffic-evidence", "requirement": success_body, "valuesPrinted": False, }, "modelRouting400Failover": { "ok": model_routing_400.get("required") is True, "required": model_routing_400.get("required"), "outcome": "yaml-rules-present-runtime-observed-by-real-traffic", "requirement": model_routing_400, "evidence": "Use Sub2API source review plus validation.gatewayResponsesRecent and Artificer/real request ids for runtime proof; default validate does not create mock upstreams or temporary failover accounts.", "valuesPrinted": False, }, "valuesPrinted": False, } def app_pod_runtime_image(): if RUNTIME_MODE == "host-docker": proc = docker(["inspect", HOST_DOCKER_APP_CONTAINER]) if proc.returncode != 0: return { "container": HOST_DOCKER_APP_CONTAINER, "error": text(proc.stderr, 1000) or text(proc.stdout, 1000), } try: data = json.loads(proc.stdout.decode("utf-8")) item = data[0] if isinstance(data, list) and data else {} except Exception as exc: return {"container": HOST_DOCKER_APP_CONTAINER, "error": str(exc)} state = item.get("State") if isinstance(item, dict) and isinstance(item.get("State"), dict) else {} health = state.get("Health") if isinstance(state.get("Health"), dict) else {} config = item.get("Config") if isinstance(item, dict) and isinstance(item.get("Config"), dict) else {} return { "container": HOST_DOCKER_APP_CONTAINER, "id": (item.get("Id") or "")[:12] if isinstance(item.get("Id"), str) else None, "image": config.get("Image"), "imageID": item.get("Image"), "ready": state.get("Running") is True and (not health or health.get("Status") in (None, "healthy")), "restartCount": item.get("RestartCount"), "startedAt": state.get("StartedAt"), "health": health.get("Status"), } try: pod = kube_json(["-n", NAMESPACE, "get", "pod", APP_POD], f"pod/{APP_POD}") spec_containers = ((pod.get("spec") or {}).get("containers") or []) if isinstance(pod, dict) else [] status_containers = ((pod.get("status") or {}).get("containerStatuses") or []) if isinstance(pod, dict) else [] spec = next((item for item in spec_containers if item.get("name") == "sub2api"), spec_containers[0] if spec_containers else {}) status = next((item for item in status_containers if item.get("name") == "sub2api"), status_containers[0] if status_containers else {}) return { "pod": APP_POD, "image": spec.get("image"), "imageID": status.get("imageID"), "ready": status.get("ready"), "restartCount": status.get("restartCount"), } except Exception as exc: return {"pod": APP_POD, "error": str(exc)} def get_account_detail(token, account): account_id = account.get("id") if isinstance(account, dict) else None if account_id is None: return account data = ensure_success(curl_api("GET", f"/api/v1/admin/accounts/{account_id}", bearer=token), f"get account {account.get('name')}") return data if isinstance(data, dict) else account def account_temp_unschedulable_status(token): accounts = list_accounts(token) by_name = {item.get("name"): item for item in accounts if isinstance(item.get("name"), str)} items = [] missing = [] mismatched = [] enabled_names = [] for name in sorted(EXPECTED_ACCOUNT_TEMP_UNSCHEDULABLE): expected = normalize_temp_unschedulable_credentials(EXPECTED_ACCOUNT_TEMP_UNSCHEDULABLE[name]) account = by_name.get(name) if account is None: missing.append(name) items.append({ "accountName": name, "accountId": None, "expectedEnabled": expected["enabled"], "runtimeEnabled": None, "expectedRuleCount": len(expected["rules"]), "runtimeRuleCount": None, "ok": False, }) continue detail = get_account_detail(token, account) credentials = detail.get("credentials") if isinstance(detail.get("credentials"), dict) else {} runtime = normalize_temp_unschedulable_credentials(credentials) temp_until = detail.get("temp_unschedulable_until") or detail.get("tempUnschedulableUntil") temp_reason = detail.get("temp_unschedulable_reason") or detail.get("tempUnschedulableReason") or "" ok = runtime == expected if expected["enabled"]: enabled_names.append(name) if not ok: mismatched.append(name) items.append({ "accountName": name, "accountId": account.get("id"), "expectedEnabled": expected["enabled"], "runtimeEnabled": runtime["enabled"], "expectedRuleCount": len(expected["rules"]), "runtimeRuleCount": len(runtime["rules"]), "expectedRules": summarize_temp_unschedulable_rules(expected["rules"]), "runtimeRules": summarize_temp_unschedulable_rules(runtime["rules"]), "status": account.get("status"), "schedulable": account.get("schedulable"), "tempUnschedulableUntil": temp_until, "tempUnschedulableReasonPreview": text(str(temp_reason), 500) if temp_reason else "", "tempUnschedulableSet": temp_until is not None or bool(temp_reason), "ok": ok, }) return { "ok": len(missing) == 0 and len(mismatched) == 0, "desired": len(EXPECTED_ACCOUNT_TEMP_UNSCHEDULABLE), "enabled": enabled_names, "missing": missing, "mismatched": mismatched, "items": items, "valuesPrinted": False, } def account_capacity_status(token): accounts = list_accounts(token) by_name = {item.get("name"): item for item in accounts if isinstance(item.get("name"), str)} items = [] missing = [] mismatched = [] expected_capacity_total = 0 runtime_concurrency_total = 0 schedulable_runtime_concurrency_total = 0 available_runtime_concurrency_total = 0 temp_unschedulable_runtime_concurrency_total = 0 for name in sorted(EXPECTED_ACCOUNT_CAPACITIES): expected = int(EXPECTED_ACCOUNT_CAPACITIES[name]) expected_capacity_total += expected account = by_name.get(name) if account is None: missing.append(name) items.append({ "accountName": name, "accountId": None, "expectedCapacity": expected, "runtimeConcurrency": None, "ok": False, }) continue runtime = account.get("concurrency") runtime_int = runtime if isinstance(runtime, int) else 0 runtime_concurrency_total += runtime_int if account.get("schedulable") is True: runtime_status = account.get("status") if runtime_status is None or runtime_status == "active": runtime_status_ok = True else: runtime_status_ok = False if runtime_status_ok: schedulable_runtime_concurrency_total += runtime_int temp_until = account.get("temp_unschedulable_until") or account.get("tempUnschedulableUntil") temp_reason = account.get("temp_unschedulable_reason") or account.get("tempUnschedulableReason") if temp_until is None and not bool(temp_reason): available_runtime_concurrency_total += runtime_int else: temp_unschedulable_runtime_concurrency_total += runtime_int ok = runtime == expected if not ok: mismatched.append(name) items.append({ "accountName": name, "accountId": account.get("id"), "expectedCapacity": expected, "runtimeConcurrency": runtime, "priority": account.get("priority"), "status": account.get("status"), "schedulable": account.get("schedulable"), "ok": ok, }) return { "ok": len(missing) == 0 and len(mismatched) == 0, "defaultAccountCapacity": POOL_DEFAULT_ACCOUNT_CAPACITY, "desired": len(EXPECTED_ACCOUNT_CAPACITIES), "totals": { "expectedCapacityTotal": expected_capacity_total, "runtimeConcurrencyTotal": runtime_concurrency_total, "schedulableRuntimeConcurrencyTotal": schedulable_runtime_concurrency_total, "availableRuntimeConcurrencyTotal": available_runtime_concurrency_total, "tempUnschedulableRuntimeConcurrencyTotal": temp_unschedulable_runtime_concurrency_total, "minOwnerConcurrency": MIN_OWNER_CONCURRENCY, "minOwnerConcurrencySource": MIN_OWNER_CONCURRENCY_SOURCE, }, "missing": missing, "mismatched": mismatched, "items": items, "valuesPrinted": False, } def account_load_factor_status(token): accounts = list_accounts(token) by_name = {item.get("name"): item for item in accounts if isinstance(item.get("name"), str)} items = [] missing = [] mismatched = [] for name in sorted(EXPECTED_ACCOUNT_LOAD_FACTORS): expected = int(EXPECTED_ACCOUNT_LOAD_FACTORS[name]) account = by_name.get(name) if account is None: missing.append(name) items.append({ "accountName": name, "accountId": None, "expectedLoadFactor": expected, "runtimeLoadFactor": None, "ok": False, }) continue runtime_raw = account.get("load_factor") if runtime_raw is None: detail = get_account_detail(token, account) runtime_raw = detail.get("load_factor") if isinstance(detail, dict) else None try: runtime = int(runtime_raw) except Exception: runtime = runtime_raw ok = runtime == expected if not ok: mismatched.append(name) items.append({ "accountName": name, "accountId": account.get("id"), "expectedLoadFactor": expected, "runtimeLoadFactor": runtime, "priority": account.get("priority"), "status": account.get("status"), "schedulable": account.get("schedulable"), "ok": ok, }) return { "ok": len(missing) == 0 and len(mismatched) == 0, "defaultAccountLoadFactor": POOL_DEFAULT_ACCOUNT_LOAD_FACTOR, "desired": len(EXPECTED_ACCOUNT_LOAD_FACTORS), "missing": missing, "mismatched": mismatched, "items": items, "valuesPrinted": False, } def account_ws_v2_status(token): accounts = list_accounts(token) by_name = {item.get("name"): item for item in accounts if isinstance(item.get("name"), str)} items = [] missing = [] mismatched = [] enabled_names = [] unschedulable_enabled = [] schedulable_enabled = [] for name in sorted(EXPECTED_ACCOUNT_WS_MODES): expected_mode = EXPECTED_ACCOUNT_WS_MODES[name] expected_enabled = expected_mode not in (None, "", "off") account = by_name.get(name) if account is None: missing.append(name) items.append({ "accountName": name, "accountId": None, "expectedMode": expected_mode, "expectedEnabled": expected_enabled, "runtimeMode": None, "runtimeEnabled": None, "ok": False, }) continue extra = account.get("extra") if isinstance(account.get("extra"), dict) else {} runtime_mode = extra.get("openai_apikey_responses_websockets_v2_mode") runtime_enabled = extra.get("openai_apikey_responses_websockets_v2_enabled") schedulable = account.get("schedulable") if expected_mode == "off": ok = runtime_mode == "off" and runtime_enabled is False elif expected_mode is None: ok = runtime_mode in (None, "", "off") and runtime_enabled in (None, False) else: ok = runtime_mode == expected_mode and runtime_enabled is True if not ok: mismatched.append(name) if expected_enabled: enabled_names.append(name) if schedulable is True: schedulable_enabled.append(name) else: unschedulable_enabled.append(name) items.append({ "accountName": name, "accountId": account.get("id"), "expectedMode": expected_mode, "expectedEnabled": expected_enabled, "runtimeMode": runtime_mode, "runtimeEnabled": runtime_enabled, "status": account.get("status"), "schedulable": schedulable, "ok": ok and ((not expected_enabled) or schedulable is True), }) availability_ok = len(enabled_names) == 0 or len(schedulable_enabled) > 0 return { "ok": len(missing) == 0 and len(mismatched) == 0 and availability_ok, "desired": len(EXPECTED_ACCOUNT_WS_MODES), "enabled": enabled_names, "schedulableEnabled": schedulable_enabled, "unschedulableEnabled": unschedulable_enabled, "missing": missing, "mismatched": mismatched, "items": items, "valuesPrinted": False, } def api_key_preview(api_key): if len(api_key) <= 14: return "***" return api_key[:10] + "..." + api_key[-4:] def secret_fingerprint(value): return "sha256:" + hashlib.sha256(value.encode("utf-8")).hexdigest()[:16] def run_sync(): global MANUAL_ACCOUNT_PROTECTIONS payload = json.loads(base64.b64decode(PAYLOAD_B64).decode("utf-8")) manual_accounts_payload = payload.get("manualAccounts") if isinstance(payload.get("manualAccounts"), dict) else {} resolved_manual_protections = manual_accounts_payload.get("protected") if not isinstance(resolved_manual_protections, list): raise RuntimeError("sync payload has no manualAccounts.protected binding plan") MANUAL_ACCOUNT_PROTECTIONS = resolved_manual_protections profiles = payload.get("profiles") or [] prune_removed = bool(payload.get("pruneRemoved")) sentinel_payload = payload.get("sentinel") if isinstance(payload.get("sentinel"), dict) else {} if not profiles and not MANUAL_ACCOUNT_PROTECTIONS: raise RuntimeError("sync payload has no profiles and no manualAccounts.protected binding plan") admin_email, token, admin_compliance = login() group, group_action = ensure_group(token) group_id = group.get("id") if isinstance(group, dict) else None if group_id is None: raise RuntimeError("pool group id missing after ensure") existing_accounts = list_accounts(token) planned_account_results = planned_sentinel_account_results(profiles, existing_accounts) sentinel_quality_prepare = ensure_sentinel_state_for_sync(planned_account_results, True) if sentinel_quality_prepare.get("ok") is not True: raise RuntimeError("prepare sentinel pending probe failed: " + json.dumps(sentinel_quality_prepare, ensure_ascii=False)) protected_frozen_names = active_sentinel_quarantine_names() account_results, pruned_account_results = ensure_accounts(token, profiles, group_id, prune_removed, protected_frozen_names, existing_accounts) manual_account_proxy_bindings = ensure_manual_account_proxy_bindings(token) manual_account_group_bindings = ensure_manual_account_group_bindings(token, group_id) manual_account_protections = manual_account_protection_status(token, group_id) capacity_status = account_capacity_status(token) load_factor_status = account_load_factor_status(token) ws_v2_status = account_ws_v2_status(token) temp_unschedulable_status = account_temp_unschedulable_status(token) api_key, secret_action, secret_apply_stdout = ensure_api_key_secret(group_id, token) api_key_result = ensure_sub2api_api_key(token, api_key, group_id) owner_balance = ensure_pool_owner_balance(token, api_key_result["userId"]) owner_concurrency = ensure_pool_owner_concurrency(token, api_key_result["userId"]) gateway = validate_gateway(api_key) responses_smoke = validate_gateway_responses(api_key) compact_evidence = recent_compact_gateway_evidence() responses_evidence = recent_responses_gateway_evidence() runtime_capabilities = validate_runtime_capabilities(token) sentinel = apply_sentinel_manifest(sentinel_payload.get("manifest")) sentinel_quality = ensure_sentinel_state_for_sync(account_results) sentinel_reassert = reassert_sentinel_freezes_after_sync(token) return { "ok": gateway["ok"] is True and responses_smoke["ok"] is True and owner_concurrency["ok"] is True and capacity_status["ok"] is True and load_factor_status["ok"] is True and ws_v2_status["ok"] is True and temp_unschedulable_status["ok"] is True and manual_account_proxy_bindings.get("ok") is True and manual_account_group_bindings.get("ok") is True and manual_account_protections.get("ok") is True and sentinel.get("ok") is True and sentinel_quality_prepare.get("ok") is True and sentinel_quality.get("ok") is True and sentinel_reassert.get("ok") is True and runtime_capabilities.get("ok") is True, "degraded": bool(responses_smoke.get("degraded")) or bool(compact_evidence.get("degraded")) or bool(responses_evidence.get("degraded")) or runtime_capabilities.get("ok") is not True, "mode": "sync", "namespace": NAMESPACE, "serviceDns": SERVICE_DNS, "appPod": APP_POD, "admin": {"email": admin_email, "tokenPrinted": False, "compliance": admin_compliance}, "pool": {"name": POOL_GROUP_NAME, "id": group_id, "action": group_action, "platform": group.get("platform") if isinstance(group, dict) else "openai"}, "accounts": { "desired": len(profiles), "created": sum(1 for item in account_results if item["action"] == "created"), "updated": sum(1 for item in account_results if item["action"] == "updated"), "pruned": len(pruned_account_results), "pruneMode": "explicit" if prune_removed else "disabled-by-default", "items": account_results, "prunedItems": pruned_account_results, "processControl": {"schedulableRestore": "sentinel marker probe only; sync does not restore schedulable for existing accounts", "durableConfig": False}, "valuesPrinted": False, }, "manualAccounts": {**manual_account_protections, "proxySync": manual_account_proxy_bindings, "groupSync": manual_account_group_bindings}, "capacity": capacity_status, "loadFactor": load_factor_status, "webSocketsV2": ws_v2_status, "tempUnschedulable": temp_unschedulable_status, "apiKey": { "name": POOL_API_KEY_NAME, "secret": pool_api_key_secret_location(), "secretAction": secret_action, "secretApply": secret_apply_stdout, "sub2apiAction": api_key_result["action"], "sub2apiId": api_key_result["id"], "groupId": api_key_result["groupId"], "userId": api_key_result["userId"], "apiKeyFingerprint": secret_fingerprint(api_key), "valuesPrinted": False, }, "ownerBalance": owner_balance, "ownerConcurrency": owner_concurrency, "sentinel": {**sentinel, "qualityGatePrepare": sentinel_quality_prepare, "qualityGate": sentinel_quality, "freezeReassert": sentinel_reassert}, "runtimeCapabilities": runtime_capabilities, "validation": {"gatewayModels": gateway, "gatewayResponses": responses_smoke, "gatewayResponsesRecent": responses_evidence, "gatewayCompactRecent": compact_evidence}, } def run_validate(): api_key = decode_secret_value(POOL_API_KEY_SECRET_NAME, POOL_API_KEY_SECRET_KEY) if not api_key: raise RuntimeError(f"{POOL_API_KEY_SECRET_NAME}.{POOL_API_KEY_SECRET_KEY} missing") admin_email, token, admin_compliance = login() key_item = next((item for item in list_user_keys(token) if item.get("key") == api_key), None) owner_balance = None owner_concurrency = None if key_item is not None and key_item.get("user_id") is not None: owner_balance = ensure_pool_owner_balance(token, key_item["user_id"]) owner_concurrency = ensure_pool_owner_concurrency(token, key_item["user_id"]) capacity_status = account_capacity_status(token) load_factor_status = account_load_factor_status(token) ws_v2_status = account_ws_v2_status(token) temp_unschedulable_status = account_temp_unschedulable_status(token) pool_group_id = key_item.get("group_id") if isinstance(key_item, dict) else None manual_account_protections = manual_account_protection_status(token, pool_group_id) gateway = validate_gateway(api_key) responses_smoke = validate_gateway_responses(api_key) compact_evidence = recent_compact_gateway_evidence() responses_evidence = recent_responses_gateway_evidence() runtime_capabilities = validate_runtime_capabilities(token) sentinel = sentinel_runtime_status() return { "ok": gateway["ok"] is True and responses_smoke["ok"] is True and (owner_concurrency is None or owner_concurrency["ok"] is True) and capacity_status["ok"] is True and load_factor_status["ok"] is True and ws_v2_status["ok"] is True and temp_unschedulable_status["ok"] is True and manual_account_protections.get("ok") is True and sentinel.get("ok") is True and runtime_capabilities.get("ok") is True, "degraded": bool(responses_smoke.get("degraded")) or bool(compact_evidence.get("degraded")) or bool(responses_evidence.get("degraded")) or runtime_capabilities.get("ok") is not True, "mode": "validate", "namespace": NAMESPACE, "serviceDns": SERVICE_DNS, "appPod": APP_POD, "admin": {"email": admin_email, "tokenPrinted": False, "compliance": admin_compliance}, "apiKey": { "secret": pool_api_key_secret_location(), "sub2apiId": key_item.get("id") if isinstance(key_item, dict) else None, "userId": key_item.get("user_id") if isinstance(key_item, dict) else None, "groupId": key_item.get("group_id") if isinstance(key_item, dict) else None, "apiKeyFingerprint": secret_fingerprint(api_key), "valuesPrinted": False, }, "ownerBalance": owner_balance, "ownerConcurrency": owner_concurrency, "capacity": capacity_status, "loadFactor": load_factor_status, "webSocketsV2": ws_v2_status, "tempUnschedulable": temp_unschedulable_status, "manualAccounts": manual_account_protections, "sentinel": sentinel, "runtimeCapabilities": runtime_capabilities, "validation": {"gatewayModels": gateway, "gatewayResponses": responses_smoke, "gatewayResponsesRecent": responses_evidence, "gatewayCompactRecent": compact_evidence}, } def parse_log_line(line): json_start = line.find("{") if json_start < 0: return None prefix = line[:json_start].rstrip() try: item = json.loads(line[json_start:]) except Exception: return None if not isinstance(item, dict): return None at = None parts = prefix.split() if parts: at = parts[0] message = "" if len(parts) >= 4: message = " ".join(parts[3:]) elif len(parts) >= 3: message = parts[2] elif len(parts) >= 1: message = parts[-1] item["_at"] = at item["_message"] = message item["_line"] = line return item def log_time_epoch(item): at = item.get("_at") if isinstance(item, dict) else None if not isinstance(at, str) or not at: return None try: return datetime.strptime(at, "%Y-%m-%dT%H:%M:%S.%f%z").timestamp() except Exception: try: return datetime.fromisoformat(at.replace("Z", "+00:00")).timestamp() except Exception: return None def event_base(item, account_names_by_id): account_id = item.get("account_id") if isinstance(account_id, str) and account_id.isdigit(): account_id = int(account_id) account_name = account_names_by_id.get(account_id) return { "at": item.get("_at"), "message": item.get("_message"), "requestId": item.get("request_id"), "clientRequestId": item.get("client_request_id"), "path": item.get("path"), "method": item.get("method"), "model": item.get("model"), "accountId": account_id, "accountName": account_name, "statusCode": item.get("status_code"), "upstreamStatus": item.get("upstream_status"), "latencyMs": item.get("latency_ms"), } def classify_trace_event(item, account_names_by_id): message = str(item.get("_message") or "") event = event_base(item, account_names_by_id) if "content_moderation.gateway_check_start" in message: event.update({ "type": "request-start", "stream": item.get("stream"), "bodyBytes": item.get("body_bytes"), "groupId": item.get("group_id"), "groupName": item.get("group_name"), "apiKeyName": item.get("api_key_name"), }) elif "content_moderation.gateway_check_done" in message: event.update({ "type": "gateway-check", "allowed": item.get("allowed"), "blocked": item.get("blocked"), "action": item.get("action"), }) elif "openai.upstream_failover_switching" in message: event.update({ "type": "failover", "switchCount": item.get("switch_count"), "maxSwitches": item.get("max_switches"), }) elif "openai.account_select_failed" in message: event.update({ "type": "select-failed", "error": item.get("error"), "excludedAccountCount": item.get("excluded_account_count"), }) elif "account_upstream_error" in message: event.update({ "type": "upstream-error", "error": item.get("error"), }) elif "account_temp_unschedulable" in message: event.update({ "type": "temp-unschedulable", "until": item.get("until") or item.get("temp_unschedulable_until"), "ruleIndex": item.get("rule_index"), "matchedKeyword": item.get("matched_keyword"), "reason": item.get("reason") or item.get("error"), }) elif "http request completed" in message: event.update({ "type": "final", "clientIp": item.get("client_ip"), "protocol": item.get("protocol"), "platform": item.get("platform"), "completedAt": item.get("completed_at"), }) elif "admin account schedulable updated" in message or "account schedulable updated" in message or "/schedulable" in str(item.get("path") or ""): event.update({ "type": "admin-schedulable", "schedulable": item.get("schedulable"), }) else: event.update({"type": "other"}) return event def with_trace_phase(event, first_epoch, last_epoch): epoch = None at = event.get("at") if isinstance(event, dict) else None if isinstance(at, str) and at: epoch = log_time_epoch({"_at": at}) if epoch is None or first_epoch is None: phase = "unknown" elif epoch < first_epoch: phase = "before-request" elif last_epoch is not None and epoch > last_epoch: phase = "after-request" else: phase = "during-request" event["phase"] = phase return event def account_snapshot_from_runtime(token): try: accounts = list_accounts(token) except Exception as exc: return [], {"error": str(exc)} rows = [] for item in accounts: if not isinstance(item, dict): continue rows.append({ "accountId": item.get("id"), "accountName": item.get("name"), "schedulable": item.get("schedulable"), "status": item.get("status"), "concurrency": item.get("concurrency"), "priority": item.get("priority"), "tempUnschedulableUntil": item.get("temp_unschedulable_until") or item.get("tempUnschedulableUntil"), "tempUnschedulableSet": (item.get("temp_unschedulable_until") or item.get("tempUnschedulableUntil")) is not None or bool(item.get("temp_unschedulable_reason") or item.get("tempUnschedulableReason")), }) rows.sort(key=lambda row: (str(row.get("accountName") or ""), int(row.get("accountId") or 0))) return rows, None def trace_reason(events, final_event): failovers = [item for item in events if item.get("type") == "failover"] select_failures = [item for item in events if item.get("type") == "select-failed"] upstream_errors = [item for item in events if item.get("type") == "upstream-error"] if failover_budget_exhausted(failovers, final_event): return "failover-budget-exhausted" if failovers and select_failures: return "failover-attempted-no-candidate" if failovers: return "failover-attempted" if select_failures: return "account-select-failed" if upstream_errors: return "upstream-error" if isinstance(final_event, dict) and isinstance(final_event.get("statusCode"), int) and final_event.get("statusCode") >= 400: return "final-http-error" if isinstance(final_event, dict) and isinstance(final_event.get("statusCode"), int): return "completed" return "unknown" def failover_budget_exhausted(failovers, final_event): if not failovers or not isinstance(final_event, dict): return False last = failovers[-1] switch_count = last.get("switchCount") max_switches = last.get("maxSwitches") final_status = final_event.get("statusCode") return ( isinstance(switch_count, int) and isinstance(max_switches, int) and max_switches > 0 and switch_count >= max_switches and isinstance(final_status, int) and final_status >= 500 ) def trace_untried_schedulable_accounts(failovers, final_event, account_snapshot): if not failover_budget_exhausted(failovers, final_event): return [] tried = set() for item in failovers: account_id = item.get("accountId") if isinstance(account_id, int): tried.add(account_id) final_account = final_event.get("accountId") if isinstance(final_event, dict) else None if isinstance(final_account, int): tried.add(final_account) result = [] for item in account_snapshot: account_id = item.get("accountId") if not isinstance(account_id, int) or account_id in tried: continue if item.get("schedulable") is True and item.get("status") == "active" and item.get("tempUnschedulableSet") is not True: result.append({ "accountId": account_id, "accountName": item.get("accountName"), "priority": item.get("priority"), "concurrency": item.get("concurrency"), }) return result def run_trace(): payload = json.loads(base64.b64decode(PAYLOAD_B64).decode("utf-8")) if PAYLOAD_B64 else {} request_id = payload.get("requestId") since = payload.get("since") or "24h" tail = int(payload.get("tail") or 20000) context_seconds = int(payload.get("contextSeconds") or 300) show_lines = bool(payload.get("showLines")) if not isinstance(request_id, str) or not request_id: raise RuntimeError("trace payload missing requestId") admin_email, token, admin_compliance = login() account_snapshot, account_snapshot_error = account_snapshot_from_runtime(token) account_names_by_id = {} for row in account_snapshot: account_id = row.get("accountId") if isinstance(account_id, str) and account_id.isdigit(): account_id = int(account_id) if isinstance(account_id, int) and isinstance(row.get("accountName"), str): account_names_by_id[account_id] = row.get("accountName") proc = kubectl(["-n", NAMESPACE, "logs", "deployment/sub2api", f"--since={since}", f"--tail={tail}"]) stdout = proc.stdout.decode("utf-8", errors="replace") parsed_lines = [] matched = [] for line in stdout.splitlines(): parsed = parse_log_line(line) if parsed is None: continue parsed_lines.append(parsed) if request_id in line: matched.append(parsed) first_epoch = None last_epoch = None for item in matched: epoch = log_time_epoch(item) if epoch is None: continue first_epoch = epoch if first_epoch is None else min(first_epoch, epoch) last_epoch = epoch if last_epoch is None else max(last_epoch, epoch) window_lines = [] if first_epoch is not None: start_epoch = first_epoch - context_seconds end_epoch = (last_epoch if last_epoch is not None else first_epoch) + context_seconds for item in parsed_lines: epoch = log_time_epoch(item) if epoch is not None and start_epoch <= epoch <= end_epoch: window_lines.append(item) else: window_lines = matched events = [classify_trace_event(item, account_names_by_id) for item in matched] request_start = next((item for item in events if item.get("type") == "request-start"), None) final_event = next((item for item in reversed(events) if item.get("type") == "final"), None) failovers = [item for item in events if item.get("type") == "failover"] select_failures = [item for item in events if item.get("type") == "select-failed"] upstream_errors = [item for item in events if item.get("type") == "upstream-error"] temp_unsched = [with_trace_phase(classify_trace_event(item, account_names_by_id), first_epoch, last_epoch) for item in window_lines if "account_temp_unschedulable" in str(item.get("_message") or "")] admin_sched = [with_trace_phase(classify_trace_event(item, account_names_by_id), first_epoch, last_epoch) for item in window_lines if ("schedulable" in str(item.get("_message") or "") or "/schedulable" in str(item.get("path") or ""))] window_events = [classify_trace_event(item, account_names_by_id) for item in window_lines] final_errors = [item for item in window_events if item.get("type") == "final" and isinstance(item.get("statusCode"), int) and item.get("statusCode") >= 400] window_failovers = [item for item in window_events if item.get("type") == "failover"] window_select_failures = [item for item in window_events if item.get("type") == "select-failed"] untried_schedulable_accounts = trace_untried_schedulable_accounts(failovers, final_event or {}, account_snapshot) reason = trace_reason(events, final_event) if not matched: outcome = "not-found" elif isinstance(final_event, dict) and isinstance(final_event.get("statusCode"), int) and final_event.get("statusCode") < 400: outcome = "succeeded" elif isinstance(final_event, dict): outcome = "failed" else: outcome = "incomplete" return { "ok": proc.returncode == 0 and len(matched) > 0, "mode": "trace", "namespace": NAMESPACE, "serviceDns": SERVICE_DNS, "appPod": APP_POD, "admin": {"email": admin_email, "tokenPrinted": False, "compliance": admin_compliance}, "requestId": request_id, "summary": { "outcome": outcome, "reason": reason, "eventCount": len(events), "matchedLineCount": len(matched), "firstAt": events[0].get("at") if events else None, "lastAt": events[-1].get("at") if events else None, }, "window": { "since": since, "tail": tail, "beforeSeconds": context_seconds, "afterSeconds": context_seconds, "lineCount": len(window_lines), }, "request": request_start or {}, "final": final_event or {}, "events": events, "failovers": failovers, "selectFailures": select_failures, "upstreamErrors": upstream_errors, "tempUnschedulable": temp_unsched, "adminSchedulable": admin_sched[-20:], "windowStats": { "matchedLines": len(matched), "eventCount": len(window_events), "finalErrorCount": len(final_errors), "failoverCount": len(window_failovers), "selectFailedCount": len(window_select_failures), "tempUnschedulableCount": len(temp_unsched), "adminSchedulableCount": len(admin_sched), }, "diagnostics": { "failoverBudgetExhausted": failover_budget_exhausted(failovers, final_event or {}), "untriedSchedulableAccounts": untried_schedulable_accounts, }, "accountSnapshot": account_snapshot, "accountSnapshotError": account_snapshot_error, "rawLines": [{"line": item.get("_line")} for item in matched[-30:]] if show_lines else [], "showLines": show_lines, "logs": { "exitCode": proc.returncode, "stderrTail": text(proc.stderr, 1000), "stdoutLineCount": len(stdout.splitlines()), }, "valuesPrinted": False, } def parse_embedded_json(stdout): if not isinstance(stdout, str) or not stdout.strip(): return None start = stdout.find("{") end = stdout.rfind("}") if start < 0 or end <= start: return None try: return json.loads(stdout[start:end + 1]) except Exception: return None def inject_env(template, name, value): spec = template.setdefault("spec", {}) containers = spec.setdefault("containers", []) if not containers: raise RuntimeError("sentinel job template has no containers") container = containers[0] env = container.setdefault("env", []) env[:] = [item for item in env if not (isinstance(item, dict) and item.get("name") == name)] env.append({"name": name, "value": value}) def sentinel_probe_job_manifest(accounts): cronjob_name = SENTINEL_CONFIG.get("cronJobName") if not isinstance(cronjob_name, str) or not cronjob_name: raise RuntimeError("sentinel cronJobName missing from config") cronjob = kube_json(["-n", NAMESPACE, "get", "cronjob", cronjob_name], f"cronjob/{cronjob_name}") job_template = ((cronjob.get("spec") or {}).get("jobTemplate") or {}) if isinstance(cronjob, dict) else {} job_spec = copy_json(job_template.get("spec") or {}) if not isinstance(job_spec, dict) or not job_spec: raise RuntimeError("sentinel CronJob jobTemplate.spec missing") template = job_spec.get("template") if not isinstance(template, dict): raise RuntimeError("sentinel CronJob jobTemplate.spec.template missing") inject_env(template, "SENTINEL_ACCOUNT_NAMES", ",".join(accounts)) job_spec["ttlSecondsAfterFinished"] = int(job_spec.get("ttlSecondsAfterFinished") or 3600) suffix = str(int(time.time()))[-8:] + "-" + "".join(secrets.choice(string.ascii_lowercase + string.digits) for _ in range(5)) job_name = ("sub2api-sentinel-probe-" + suffix)[:63] return job_name, { "apiVersion": "batch/v1", "kind": "Job", "metadata": { "name": job_name, "namespace": NAMESPACE, "labels": { "app.kubernetes.io/name": cronjob_name, "app.kubernetes.io/part-of": "platform-infra", "app.kubernetes.io/managed-by": "unidesk", "unidesk.ai/job-purpose": "sub2api-account-sentinel-manual-probe", }, }, "spec": job_spec, } def copy_json(value): return json.loads(json.dumps(value)) def job_condition(job, cond_type): for item in ((job.get("status") or {}).get("conditions") or []): if item.get("type") == cond_type and item.get("status") == "True": return item return None def wait_sentinel_probe_job(job_name, timeout_seconds): deadline = time.time() + timeout_seconds latest = None while time.time() < deadline: latest, err = safe_kube_json(["-n", NAMESPACE, "get", "job", job_name], f"job/{job_name}") if isinstance(latest, dict): complete = job_condition(latest, "Complete") failed = job_condition(latest, "Failed") if complete is not None: return "succeeded", latest, complete if failed is not None: return "failed", latest, failed time.sleep(2) return "timeout", latest, None def job_logs(job_name): proc = kubectl(["-n", NAMESPACE, "logs", f"job/{job_name}", "--tail=4000"]) return { "exitCode": proc.returncode, "stdout": proc.stdout.decode("utf-8", errors="replace"), "stderr": text(proc.stderr, 4000), } def run_sentinel_probe(): payload = json.loads(base64.b64decode(PAYLOAD_B64).decode("utf-8")) if PAYLOAD_B64 else {} accounts = payload.get("accounts") if isinstance(payload, dict) else None if not isinstance(accounts, list) or not accounts or not all(isinstance(item, str) and item for item in accounts): raise RuntimeError("sentinel-probe payload requires non-empty accounts") job_name, manifest = sentinel_probe_job_manifest(accounts) proc = kubectl(["apply", "--server-side", "--force-conflicts", f"--field-manager={FIELD_MANAGER}", "-f", "-"], json.dumps(manifest)) if proc.returncode != 0: raise RuntimeError(f"apply sentinel probe job failed: {text(proc.stderr, 2000)}") timeout_seconds = max(300, int(SENTINEL_CONFIG.get("probe", {}).get("timeoutSeconds") or 30) + 240) status, job, condition = wait_sentinel_probe_job(job_name, timeout_seconds) logs = job_logs(job_name) parsed = parse_embedded_json(logs.get("stdout") or "") results = parsed.get("results") if isinstance(parsed, dict) and isinstance(parsed.get("results"), list) else [] state_summary = sentinel_state_summary() last_run = state_summary.get("lastRun") if isinstance(state_summary, dict) and isinstance(state_summary.get("lastRun"), dict) else {} if not results and isinstance(last_run.get("results"), list): results = last_run.get("results") requested = set(accounts) measured = {item.get("accountName") for item in results if isinstance(item, dict)} missing = sorted(name for name in requested if name not in measured) marker_ok = len(missing) == 0 and all(isinstance(item, dict) and item.get("accountName") in requested and item.get("markerMatched") is True for item in results if isinstance(item, dict) and item.get("accountName") in requested) if not results and isinstance(last_run, dict): selected = int(last_run.get("selected") or 0) ok_count = int(last_run.get("okCount") or 0) marker_mismatch_count = int(last_run.get("markerMismatchCount") or 0) transport_failure_count = int(last_run.get("transportFailureCount") or 0) if selected >= len(requested) and ok_count >= len(requested) and marker_mismatch_count == 0 and transport_failure_count == 0: missing = [] marker_ok = True job_ok = status == "succeeded" and logs.get("exitCode") == 0 return { "ok": job_ok and marker_ok, "jobExecutionOk": job_ok, "markerOk": marker_ok, "mode": "sentinel-probe", "namespace": NAMESPACE, "requestedAccounts": accounts, "missingAccounts": missing, "job": { "name": job_name, "status": status, "condition": condition, "timeoutSeconds": timeout_seconds, "applyStdoutTail": text(proc.stdout, 1200), "logsExitCode": logs.get("exitCode"), "logsStderrTail": logs.get("stderr"), }, "probe": parsed, "summary": { "at": last_run.get("at"), "selected": last_run.get("selected"), "okCount": last_run.get("okCount"), "markerMismatchCount": last_run.get("markerMismatchCount"), "transportFailureCount": last_run.get("transportFailureCount"), "actionsTaken": last_run.get("actionsTaken"), "selection": last_run.get("selection"), }, "results": results, "sentinelState": state_summary, "valuesPrinted": False, } def run_cleanup_probes(): admin_email, token, admin_compliance = login() cleanup = cleanup_probe_resources(token) return { "ok": cleanup.get("ok") is True, "mode": "cleanup-probes", "namespace": NAMESPACE, "serviceDns": SERVICE_DNS, "appPod": APP_POD, "admin": {"email": admin_email, "tokenPrinted": False, "compliance": admin_compliance}, "cleanup": cleanup, "valuesPrinted": False, } try: if MODE == "sync": result = run_sync() elif MODE == "trace": result = run_trace() elif MODE == "cleanup-probes": result = run_cleanup_probes() elif MODE == "sentinel-probe": result = run_sentinel_probe() else: result = run_validate() except Exception as exc: result = { "ok": False, "mode": MODE, "namespace": NAMESPACE, "serviceDns": SERVICE_DNS, "appPod": globals().get("APP_POD"), "error": str(exc), "valuesPrinted": False, } print(json.dumps(result, ensure_ascii=False, indent=2)) sys.exit(0 if result.get("ok") else 1) PY `; }