From 2be7295d53b264bcd998410b86cdc04e8ce64708 Mon Sep 17 00:00:00 2001 From: xiayu Date: Wed, 10 Jun 2026 00:57:49 +0800 Subject: [PATCH] chore: harden 0.6.3 publication gates --- Makefile | 7 ++- README.zh-CN.md | 11 +++- scripts/check_publication_state.py | 98 ++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 scripts/check_publication_state.py diff --git a/Makefile b/Makefile index cc5bacf..db2a601 100644 --- a/Makefile +++ b/Makefile @@ -88,7 +88,7 @@ install-webui: STATIC_DIR = ksadk/server/static OPEN_SOURCE_SMOKE_VENV ?= /tmp/ksadk-open-source-smoke -OPEN_SOURCE_SMOKE_WHEEL := dist/ksadk-0.6.2-py3-none-any.whl +OPEN_SOURCE_SMOKE_WHEEL ?= dist/ksadk-$(VERSION)-py3-none-any.whl build-webui: @echo "ℹ️ ksadk-python 不包含可编辑 Web UI 源码。" @@ -224,6 +224,7 @@ PUBLIC_DOCS_URL ?= https://kingsoftcloud.github.io/ksadk-python/ PUBLIC_PYPI_PROJECT ?= ksadk PUBLIC_ALIAS_PYPI_PROJECT ?= agentengine-sdk-python PUBLIC_RELEASE_TAG ?= v$(V) +PUBLIC_PUBLISH_PHASE ?= pre-publish public-status: @echo "==> public candidate" @@ -296,7 +297,7 @@ public-preflight: public-audit public-test public-docs-build public-build-check public-publish-check: @echo "==> publication state check" @if [ -f "scripts/check_publication_state.py" ]; then \ - uv run python scripts/check_publication_state.py --phase pre-publish; \ + uv run python scripts/check_publication_state.py --phase "$(PUBLIC_PUBLISH_PHASE)" --version "$(VERSION)"; \ else \ echo "⚠️ scripts/check_publication_state.py 不存在,执行基础 HTTP 检查"; \ python3 -c 'import json, urllib.request; targets={"repo":"$(PUBLIC_REPO)","docs":"$(PUBLIC_DOCS_URL)","pypi":"https://pypi.org/pypi/$(PUBLIC_PYPI_PROJECT)/json","alias_pypi":"https://pypi.org/pypi/$(PUBLIC_ALIAS_PYPI_PROJECT)/json"}; [print("%s: HTTP %s%s" % (name, resp.status, ("\n version=%s" % json.load(resp)["info"].get("version")) if name.endswith("pypi") else "")) for name, url in targets.items() for resp in [urllib.request.urlopen(url, timeout=20)]]'; \ @@ -304,7 +305,7 @@ public-publish-check: public-release-tag: ifndef V - $(error ❌ 请指定版本号,例如: make public-release-tag V=0.6.2) + $(error ❌ 请指定版本号,例如: make public-release-tag V=$(VERSION)) endif @branch=$$(git branch --show-current); \ if [ "$$branch" != "$(PUBLIC_BRANCH)" ]; then \ diff --git a/README.zh-CN.md b/README.zh-CN.md index dbc6809..1ea8b03 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -10,7 +10,7 @@ 本地开发、Serverless 运行时、Google ADK、LangChain/LangGraph、 DeepAgents、Hermes、OpenClaw、MCP 和 Skill Runtime 等场景。 -当前版本:`0.6.2`。 +当前版本:`0.6.3`。 ## 安装 @@ -64,6 +64,14 @@ agentengine launch . --target serverless - AgentEngine 内置工具:skill 发现/加载、workspace 文件操作、component status、sandbox status 和 sandbox direct code/command execution - Sandbox Runtime:通用沙箱抽象与 E2B 兼容后端 +## 0.6.3 重点 + +- Hosted UI 与最新 gateway / server 对齐 `/hosted-ui/chat/`、share link、SSE 订阅和 native terminal 代理契约;`agentengine dashboard open` 继续优先打开托管入口,本地 `agentengine web` 保持调试用途。 +- LangGraph runner 在工具调用后即使没有文本流式 chunk,也会输出最终 answer,避免本地 Web UI 出现空 assistant message。 +- Skill Service 支持 `KSADK_SKILL_SERVICE_REGION=pre-online` 的 KOP 路由,自动设置预发所需 header。 +- OpenClaw / Hermes 更新已有实例时默认保留服务端已有 env、storage、network、memory 配置,只在显式传入对应 CLI 参数时覆盖。 +- `ksadk.toolsets`、Tool Gateway、Skill Runtime 与 Skill Service 相关文件纳入发布包,主推 LangGraph demo 可以在干净安装后直接绑定 AgentEngine 内置工具。 + ## 0.6.2 重点 - Skill Runtime 支持 Skill Space 远端发现、按需下载、`sha256` 校验、安全解压、`SKILL.md` instruction 加载,以及 `local_process` / E2B sandbox backend workflow 执行。 @@ -90,6 +98,7 @@ agentengine launch . --target serverless - 文档: - 仓库: +- wiki: - 示例仓库: - Web UI 仓库: - PyPI: diff --git a/scripts/check_publication_state.py b/scripts/check_publication_state.py new file mode 100644 index 0000000..48c34ad --- /dev/null +++ b/scripts/check_publication_state.py @@ -0,0 +1,98 @@ +"""检查公开发布状态。 + +发布前使用 `--phase pre-publish`,确保 GitHub Pages 可访问且 PyPI 上还没有 +当前版本;发布后使用 `--phase post-publish`,确保 PyPI 已能查询到当前版本。 +""" + +from __future__ import annotations + +import argparse +import json +import sys +import urllib.error +import urllib.request +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +PYPROJECT = ROOT / "pyproject.toml" + + +def _current_version() -> str: + for line in PYPROJECT.read_text(encoding="utf-8").splitlines(): + if line.startswith("version = "): + return line.split("=", 1)[1].strip().strip('"') + raise RuntimeError("pyproject.toml 中未找到 version") + + +def _open(url: str) -> tuple[int, bytes]: + request = urllib.request.Request(url, headers={"User-Agent": "ksadk-publication-check"}) + with urllib.request.urlopen(request, timeout=20) as response: + return response.status, response.read() + + +def _expect_http_ok(name: str, url: str) -> None: + status, _ = _open(url) + if status != 200: + raise RuntimeError(f"{name}: 期望 HTTP 200,实际 {status}: {url}") + print(f"{name}: HTTP {status}") + + +def _pypi_project_version(project: str) -> str | None: + url = f"https://pypi.org/pypi/{project}/json" + try: + status, body = _open(url) + except urllib.error.HTTPError as exc: + if exc.code == 404: + return None + raise + if status != 200: + raise RuntimeError(f"{project}: 期望 HTTP 200,实际 {status}: {url}") + data = json.loads(body) + return data.get("info", {}).get("version") + + +def _pypi_version_exists(project: str, version: str) -> bool: + url = f"https://pypi.org/pypi/{project}/{version}/json" + try: + status, _ = _open(url) + except urllib.error.HTTPError as exc: + if exc.code == 404: + return False + raise + if status != 200: + raise RuntimeError(f"{project}=={version}: 期望 HTTP 200 或 404,实际 {status}") + return True + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--phase", choices=("pre-publish", "post-publish"), required=True) + parser.add_argument("--version", default=_current_version()) + parser.add_argument("--project", default="ksadk") + parser.add_argument("--alias-project", default="agentengine-sdk-python") + parser.add_argument("--docs-url", default="https://kingsoftcloud.github.io/ksadk-python/") + args = parser.parse_args() + + _expect_http_ok("docs", args.docs_url) + + latest = _pypi_project_version(args.project) + print(f"pypi:{args.project}: latest={latest}") + + exists = _pypi_version_exists(args.project, args.version) + print(f"pypi:{args.project}=={args.version}: exists={exists}") + + alias_latest = _pypi_project_version(args.alias_project) + print(f"pypi:{args.alias_project}: latest={alias_latest}") + + if args.phase == "pre-publish" and exists: + raise RuntimeError(f"发布前检查失败:PyPI 已存在 {args.project}=={args.version}") + if args.phase == "post-publish" and not exists: + raise RuntimeError(f"发布后检查失败:PyPI 尚未存在 {args.project}=={args.version}") + + print(f"✅ publication {args.phase} check passed for {args.project}=={args.version}") + return 0 + + +if __name__ == "__main__": + sys.exit(main())