# jiangchang-platform-kit ## 1. 项目简介 匠厂平台共享 Python SDK:在同一发行包中提供 **Skill 侧通用能力**(权益校验、统一日志与运行环境解析)与 **桌面应用自动化能力**(通过 CDP 连接匠厂客户端,驱动聊天与断言)。 ## 2. 安装方式 ```bash pip install jiangchang-platform-kit --index-url https://git.jc2009.com/api/packages/client-jiangchang/pypi/simple/ ``` ## 3. 包含的模块 - **jiangchang_skill_core**:权益 HTTP 客户端、`enforce_entitlement`、统一文件日志、`JIANGCHANG_*` 数据根与技能根路径解析、共享媒体资源(`media_assets`)等,供 Skill CLI / 服务进程使用。 - **jiangchang_desktop_sdk**:匠厂桌面应用自动化客户端(基于 CDP + Playwright 同步 API)。**本包不声明 Playwright 依赖**;请在运行环境中自行安装 Playwright,以便 `import playwright` 可用。 ## 4. 快速开始 ### jiangchang_skill_core ```python import os from jiangchang_skill_core import ( EntitlementClient, enforce_entitlement, setup_skill_logging, get_skill_logger, ) os.environ.setdefault("JIANGCHANG_AUTH_BASE_URL", "https://auth.example.com") os.environ.setdefault("JIANGCHANG_AUTH_API_KEY", "secret") setup_skill_logging(skill_slug="demo-skill", logger_name="demo") log = get_skill_logger() log.info("skill started") # client = EntitlementClient() # enforce_entitlement(user_id="u1", skill_slug="demo-skill") ``` ### jiangchang_desktop_sdk ```python from jiangchang_desktop_sdk import JiangchangDesktopClient, AskOptions # 需已安装 playwright,且匠厂客户端可按 CDP 端口暴露调试接口 client = JiangchangDesktopClient() client.ensure_app_running(wait_timeout_s=30.0) try: answer = client.ask("你好", options=AskOptions(new_task=True)) print(answer) finally: client.disconnect() ``` ### 桌面 E2E(`jiangchang_desktop_sdk.e2e_helpers`) Skill 的桌面端到端测试应通过宿主 IPC 获取**真实**当前用户数据目录,不要在 pytest 进程里手工设置 `JIANGCHANG_USER_ID` / `CLAW_USER_ID` 来伪造用户(否则会回退到 `_anon`)。 推荐 preflight 流程: 1. `require_logged_in_skill_data_dir(page, skill_slug)` — 经 `jiangchang:resolve-skill-data-dir` 解析路径,并断言已登录(非 `_anon` / `anon` / `anonymous`)。 2. `assert_skill_env_file(ctx, ["API_KEY", ...])` — 检查 `{skill_data}/config/.env` 中必填项已配置且非占位符;E2E 不会自动生成真实密钥。 3. `send_prompt_via_composer(page, prompt)` — 普通文本逐字键入;内容中的 `\n` / `\r\n` / `\r` 用 **Shift+Enter** 输入换行;最后用 **Enter** 发送(禁止 DOM 注入、剪贴板、JS 伪造 input/change)。 诊断时可调用 `get_runtime_env(page)` 查看主进程解析的环境,**不要**用它拼用户数据路径。 ```python from jiangchang_desktop_sdk.e2e_helpers import ( assert_skill_env_file, require_logged_in_skill_data_dir, send_prompt_via_composer, ) ctx = require_logged_in_skill_data_dir(page, "demo-skill") assert_skill_env_file(ctx, ["API_KEY"]) send_prompt_via_composer(page, "第一行\n第二行") ``` ## 共享媒体资源 `jiangchang_skill_core.media_assets` 提供共享媒体资源解析能力。默认会从: `{JIANGCHANG_DATA_ROOT}/shared/media-assets` 读取背景音乐、字体、水印和 ffmpeg 工具。 如果目录不存在,会优先下载稳定 release bundle(不依赖用户电脑安装 Git): `https://git.jc2009.com/client-commons/media-assets/releases/download/vlatest/media-assets.zip` bundle 内含背景音乐、字体与水印;**不包含** ffmpeg 二进制。ffmpeg / ffprobe 仍由 bundle 内 `manifest.json` 的 `tools.ffmpeg` 下载源单独拉取。 可通过环境变量覆盖: - `MEDIA_ASSETS_ROOT=/path/to/custom/media-assets` — 本地资源目录 - `MEDIA_ASSETS_BUNDLE_URL=https://example.com/media-assets.zip` — bundle 下载地址 ### Runtime diagnostics(统一 health 诊断) `jiangchang_skill_core.runtime_diagnostics` 提供各 Skill 共用的 Runtime / media-assets / ffmpeg 探测,避免每个技能各自复制检查逻辑。Skill 的 `health` 命令可调用: ```python from jiangchang_skill_core import ( collect_runtime_diagnostics, format_runtime_health_lines, runtime_diagnostics_dict, ) diag = collect_runtime_diagnostics( skill_slug="my-skill", platform_kit_min_version="1.0.10", # 可选;省略则只报告当前版本 skill_root="/path/to/my-skill", # 可选;用于检测 vendored jiangchang_skill_core ) for line in format_runtime_health_lines(diag): print(line) print(runtime_diagnostics_dict(diag)) # 可 JSON 序列化 ``` 诊断只读探测本地状态,不会触发 media-assets 下载。 ### 录屏合成(`screencast`) `run_screencast()` / `compose_video()` 通过 `jiangchang_skill_core.media_assets` 解析 ffmpeg 与背景音乐,**不使用系统 PATH 中的 `ffmpeg`**。 - **默认**:不传 `media_assets_root` 时,使用 `{JIANGCHANG_DATA_ROOT}/shared/media-assets`(或进程环境中已有的 `MEDIA_ASSETS_ROOT`)。 - **覆盖**:传入 `media_assets_root="/path/to/media-assets"`,或在 `compose_video(..., media_assets_env={"MEDIA_ASSETS_ROOT": "..."})` 中设置;参数 `media_assets_root` 优先于 `media_assets_env` 里已有的 `MEDIA_ASSETS_ROOT`。 - `run_screencast(media_assets_root=...)` 会把该路径原样传给 `compose_video()`;`music_subdir` 已废弃(会触发 `DeprecationWarning`),音乐选择统一由 `pick_background_music()` 完成。 ```python from screencast import run_screencast run_screencast( skill_slug="demo-skill", subtitle_script=[("PASSED", "测试通过")], pytest_args=["-q", "tests/test_demo.py"], output_dir="./out", media_assets_root="/path/to/media-assets", # 可选 ) ``` ## 5. 发版流程(维护者) ### main 分支自动预发布 每次推送到 `main` 会触发 `.github/workflows/publish.yml`,自动构建并发布 **post 预发布版本**(仅供 CI 流水线内部使用,**不是**正式版本线)。 正式版本线使用 **1.0.x** semver,通过 git tag 发布。 ```bash git push origin main ``` 测试人员升级安装(始终安装当前正式 tag 对应的版本,例如 `1.0.9`): ```bash python -m pip install --upgrade \ --index-url https://git.jc2009.com/api/packages/client-jiangchang/pypi/simple/ \ --extra-index-url https://pypi.org/simple \ jiangchang-platform-kit ``` ### 正式版本(tag) 打 tag 并推送后,发布 **去掉 `v` 前缀** 的正式版本号(例如 `v1.0.9` → PyPI 包 `1.0.9`): ```bash git tag v1.0.9 git push origin v1.0.9 ``` ### 安装后验证 ```bash python -c "from jiangchang_desktop_sdk.e2e_helpers import send_prompt_via_composer; print('e2e_helpers OK')" python -c "from screencast import run_screencast; print('screencast OK')" ``` 仓库内还保留 `examples/`、`tools/`、`python-runtime/` 目录及既有 Skill / 前端相关的 GitHub Actions 工作流,与本 PyPI 包的发布彼此独立。`tools/screencast/` 为兼容薄壳,pip 包内实现位于 `screencast` 包(`src/screencast/`)。 ## v0.2.0 — data-jcid migration (internal refactor) ### TL;DR 本次内部重构:SDK 不再依赖匠厂主仓库的 `data-jcid` 属性。 所有公开 API(`ask` / `read` / `new_task` / `wait_gateway_ready` 等) **签名与行为保持不变**,只有内部选择器实现切换到新的语义锚点 + ClawX testid。 ### 改变了什么 - `client.py` 内所有 `[data-jcid="..."]` 选择器已移除 - 改用匠厂主仓库为 SDK 承诺的语义锚点:`data-role` / `data-message-id` / `data-streaming`(在 ChatMessage 上)、`data-sending` / `data-message-count` (在 chat-page 根上) - `window.__jc_sending__` 仍是核心 sending 信号,保持不变 - 这些锚点由匠厂主仓库的以下机制守护: - `docs/JIANGCHANG_CUSTOM_ANCHORS.md` 中央清单 - 源码内 `JIANGCHANG CUSTOM ANCHOR` 边界注释 - `harness/specs/jiangchang-custom-anchors.spec.ts` E2E 守护测试 - Gitea CI grep 检查 step ### 已知限制 - `send_file()` 暂未实现,抛 `NotImplementedError`。 原因:附件上传走 Electron IPC,SDK 需要主仓库提供新 hook。 - `_gateway_state()` 永远返回 `'unknown'`。 原因:gateway 状态不再暴露 DOM data-state;本方法已预留未来通过 `window.__jc_gateway_state__` 读取的实现路径。 这两个限制对常规 ask/read 工作流**无影响**,sending 完成判定依靠 `window.__jc_sending__` + `data-sending` + 文本稳定性合流即可。 ### 升级影响 - 在 jiangchang v2.0.17(含)以上版本上跑:应正常工作 - 在 jiangchang v2.0.16(及更早含 data-jcid 的版本)上跑:不再兼容 如果需要在旧版本上跑,固定 SDK 版本到 v0.1.x ### 验证清单(集成方建议跑一次) 打开匠厂客户端 → 用 SDK 跑下面 3 步,确认基础流程通: ```python from jiangchang_desktop_sdk import JiangchangDesktopClient with JiangchangDesktopClient() as c: c.ensure_app_running() c.wait_gateway_ready() c.new_task() answer = c.ask("你好,请回复确认 SDK 可用") assert answer, f"Empty answer: {answer!r}" print("OK:", answer[:200]) ```