Files
mini-openclaw/scripts/verify_aliyun_runtime.py
T
2026-09-28 16:34:10 +08:00

149 lines
5.9 KiB
Python

"""Read-only verification of the Alibaba Cloud demo runtime.
Connection secrets are read from the ignored server-deploy.conf file. Model
credentials are read inside the running container and are never returned.
"""
from __future__ import annotations
import argparse
import json
import shlex
from pathlib import Path
import paramiko
ROOT = Path(__file__).resolve().parents[1]
def load_config(path: Path) -> dict[str, str]:
values: dict[str, str] = {}
for raw in path.read_text(encoding="utf-8-sig").splitlines():
line = raw.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
values[key.strip()] = value.strip()
return values
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--config", type=Path, default=ROOT / "server-deploy.conf")
args = parser.parse_args()
config = load_config(args.config)
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
connect = {
"hostname": config["SERVER_HOST"],
"port": int(config.get("SERVER_PORT", "22")),
"username": config["SERVER_USER"],
"timeout": 15,
"banner_timeout": 15,
"auth_timeout": 15,
}
if config.get("SERVER_KEYFILE"):
connect["key_filename"] = config["SERVER_KEYFILE"]
else:
connect["password"] = config["SERVER_PASSWORD"]
client.connect(**connect)
container_script = r'''
import json
import os
import httpx
base = os.getenv("DEEPSEEK_BASE_URL", "").rstrip("/")
key = os.getenv("DEEPSEEK_API_KEY", "")
model = os.getenv("DEEPSEEK_MODEL", "")
local_url = os.getenv("XINGBANG_LOCAL_MODEL_URL", "").rstrip("/")
local_key = os.getenv("XINGBANG_LOCAL_MODEL_KEY", "")
local_model = os.getenv("XINGBANG_LOCAL_MODEL_NAME", "")
result = {
"deepseek_base_url": base,
"official_url": base == "https://api.deepseek.com",
"deepseek_model": model,
"deepseek_key_configured": bool(key),
"business_model_url": local_url,
"business_model_name": local_model,
"business_model_key_configured": bool(local_key),
"configured_api_status": None,
"deepseek_official_api_status": None,
}
if base and key:
try:
response = httpx.post(
base + "/chat/completions",
headers={"Authorization": "Bearer " + key},
json={
"model": model or "deepseek-chat",
"messages": [{"role": "user", "content": "Reply with OK."}],
"max_tokens": 2,
"temperature": 0,
},
timeout=30,
trust_env=False,
)
result["configured_api_status"] = response.status_code
except Exception as exc:
result["configured_api_status"] = "connection_error:" + type(exc).__name__
try:
response = httpx.post(
"https://api.deepseek.com/chat/completions",
headers={"Authorization": "Bearer " + key},
json={
"model": "deepseek-chat",
"messages": [{"role": "user", "content": "Reply with OK."}],
"max_tokens": 2,
"temperature": 0,
},
timeout=30,
trust_env=False,
)
result["deepseek_official_api_status"] = response.status_code
except Exception as exc:
result["deepseek_official_api_status"] = "connection_error:" + type(exc).__name__
print(json.dumps(result, ensure_ascii=True))
'''
encoded = container_script.encode("utf-8").hex()
remote_python = (
"import os;exec(bytes.fromhex(" + repr(encoded) + ").decode('utf-8'))"
)
command = (
"set -eu; "
"printf 'container='; docker inspect -f '{{.State.Status}}' mini-openclaw; "
"printf 'health='; docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' mini-openclaw; "
"printf 'image='; docker inspect -f '{{.Config.Image}}' mini-openclaw; "
"printf 'restart_policy='; docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' mini-openclaw; "
"printf 'port_bindings='; docker inspect -f '{{json .HostConfig.PortBindings}}' mini-openclaw; "
"printf 'mounts='; docker inspect -f '{{range .Mounts}}{{if .Name}}{{.Name}}{{else}}{{.Source}}{{end}}:{{.Destination}};{{end}}' mini-openclaw; "
"printf 'networks='; docker inspect -f '{{range $name, $value := .NetworkSettings.Networks}}{{$name}};{{end}}' mini-openclaw; "
"printf 'deploy_path='; test -d " + shlex.quote(config.get("SERVER_DEPLOY_PATH", "")) + " && echo present || echo missing; "
"printf 'compose_files='; docker inspect -f '{{index .Config.Labels \"com.docker.compose.project.config_files\"}}' mini-openclaw 2>/dev/null || true; "
"printf 'working_dir='; docker inspect -f '{{index .Config.Labels \"com.docker.compose.project.working_dir\"}}' mini-openclaw 2>/dev/null || true; "
"cd " + shlex.quote(config.get("SERVER_DEPLOY_PATH", "/")) + "; "
"if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then "
"printf 'git_head='; git rev-parse --short HEAD; "
"printf 'git_branch='; git branch --show-current; "
"printf 'git_dirty_files='; git status --porcelain | wc -l; "
"else echo 'git_head=not-a-repository'; fi; "
"echo 'deploy_files_begin'; find . -maxdepth 2 -type f -printf '%P\\n' | sort | head -80; echo 'deploy_files_end'; "
"docker exec mini-openclaw python -c " + shlex.quote(remote_python)
)
_, stdout, stderr = client.exec_command(command, timeout=60)
output = stdout.read().decode("utf-8", errors="replace")
error = stderr.read().decode("utf-8", errors="replace")
code = stdout.channel.recv_exit_status()
client.close()
if code:
raise RuntimeError(error.strip() or f"Remote verification failed with exit code {code}")
print(output.strip())
return 0
if __name__ == "__main__":
raise SystemExit(main())