diff --git a/marketplace.json b/marketplace.json index b988fe5..2a64d15 100644 --- a/marketplace.json +++ b/marketplace.json @@ -117,7 +117,7 @@ "en": "Records WebM through ZCode's built-in Browser Use WebView and converts it to MP4 with ffmpeg for video/URL replication; no Playwright or external Chromium required.", "zh-CN": "基于 ZCode 内置 Browser Use WebView 录制 WebM 并用 ffmpeg 转成 MP4/URL 复刻;无需 Playwright 或外部 Chromium" }, - "version": "0.6.0", + "version": "0.6.1", "author": { "name": "Z.ai", "url": "https://z.ai" diff --git a/plugins/video2code/.claude-plugin/plugin.json b/plugins/video2code/.claude-plugin/plugin.json index cfc189f..4d55433 100644 --- a/plugins/video2code/.claude-plugin/plugin.json +++ b/plugins/video2code/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "video2code", - "version": "0.6.0", + "version": "0.6.1", "description": "基于 ZCode 内置 Browser Use WebView 录制 WebM 并用 ffmpeg 转成 MP4/URL 复刻;无需 Playwright 或外部 Chromium", "author": { "name": "Z.ai", @@ -34,8 +34,9 @@ "mcpServers": { "runtime": { "type": "stdio", - "command": "python3", + "command": "node", "args": [ + "${CLAUDE_PLUGIN_ROOT}/hooks/run_python.mjs", "${CLAUDE_PLUGIN_ROOT}/mcp/runtime_server.py" ], "cwd": "${CLAUDE_PROJECT_DIR}", @@ -47,8 +48,9 @@ }, "video": { "type": "stdio", - "command": "python3", + "command": "node", "args": [ + "${CLAUDE_PLUGIN_ROOT}/hooks/run_python.mjs", "${CLAUDE_PLUGIN_ROOT}/mcp/video_server.py" ], "cwd": "${CLAUDE_PROJECT_DIR}", diff --git a/plugins/video2code/.mcp.json b/plugins/video2code/.mcp.json index 0bb0ccc..9b4d5e3 100644 --- a/plugins/video2code/.mcp.json +++ b/plugins/video2code/.mcp.json @@ -2,8 +2,11 @@ "mcpServers": { "runtime": { "type": "stdio", - "command": "python3", - "args": ["${CLAUDE_PLUGIN_ROOT}/mcp/runtime_server.py"], + "command": "node", + "args": [ + "${CLAUDE_PLUGIN_ROOT}/hooks/run_python.mjs", + "${CLAUDE_PLUGIN_ROOT}/mcp/runtime_server.py" + ], "cwd": "${CLAUDE_PROJECT_DIR}", "env": { "V2C_PLUGIN_ROOT": "${CLAUDE_PLUGIN_ROOT}", @@ -13,8 +16,11 @@ }, "video": { "type": "stdio", - "command": "python3", - "args": ["${CLAUDE_PLUGIN_ROOT}/mcp/video_server.py"], + "command": "node", + "args": [ + "${CLAUDE_PLUGIN_ROOT}/hooks/run_python.mjs", + "${CLAUDE_PLUGIN_ROOT}/mcp/video_server.py" + ], "cwd": "${CLAUDE_PROJECT_DIR}", "env": { "V2C_PLUGIN_ROOT": "${CLAUDE_PLUGIN_ROOT}", diff --git a/plugins/video2code/.zcode-plugin/plugin.json b/plugins/video2code/.zcode-plugin/plugin.json index cfc189f..4d55433 100644 --- a/plugins/video2code/.zcode-plugin/plugin.json +++ b/plugins/video2code/.zcode-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "video2code", - "version": "0.6.0", + "version": "0.6.1", "description": "基于 ZCode 内置 Browser Use WebView 录制 WebM 并用 ffmpeg 转成 MP4/URL 复刻;无需 Playwright 或外部 Chromium", "author": { "name": "Z.ai", @@ -34,8 +34,9 @@ "mcpServers": { "runtime": { "type": "stdio", - "command": "python3", + "command": "node", "args": [ + "${CLAUDE_PLUGIN_ROOT}/hooks/run_python.mjs", "${CLAUDE_PLUGIN_ROOT}/mcp/runtime_server.py" ], "cwd": "${CLAUDE_PROJECT_DIR}", @@ -47,8 +48,9 @@ }, "video": { "type": "stdio", - "command": "python3", + "command": "node", "args": [ + "${CLAUDE_PLUGIN_ROOT}/hooks/run_python.mjs", "${CLAUDE_PLUGIN_ROOT}/mcp/video_server.py" ], "cwd": "${CLAUDE_PROJECT_DIR}", diff --git a/plugins/video2code/README.md b/plugins/video2code/README.md index 99b8c77..bb47ddd 100644 --- a/plugins/video2code/README.md +++ b/plugins/video2code/README.md @@ -69,7 +69,13 @@ Two stdio MCP servers, declared in both [`.mcp.json`](./.mcp.json) and the plugi ## Hooks -All four are `command` hooks running `python3` against scripts in [`hooks/`](./hooks). +All four are `command` hooks. They and both MCP servers use the bundled +[`run_python.mjs`](./hooks/run_python.mjs) launcher instead of assuming the host +provides a `python3` executable. On Windows it tries `py -3`, `python`, then +`python3`; on macOS and Linux it tries `python3`, then `python`. The launcher +preserves the selected interpreter's exit status and defaults Python stdio to +UTF-8 so localized Hook output is not limited by the Windows console code page. +Existing `PYTHONUTF8` and `PYTHONIOENCODING` values are respected. | Event | Script | Behavior | | --- | --- | --- | @@ -90,10 +96,12 @@ All four are `command` hooks running `python3` against scripts in [`hooks/`](./h Run the doctor rather than checking by hand: ```bash -python3 skills/env-setup/scripts/env_doctor.py -python3 skills/env-setup/scripts/env_doctor.py --fix +node hooks/run_python.mjs skills/env-setup/scripts/env_doctor.py +node hooks/run_python.mjs skills/env-setup/scripts/env_doctor.py --fix ``` +The launcher skips interpreters older than Python 3.10. Run the doctor in the same environment as ZCode: GUI and terminal `PATH` values may select different interpreters. Python repairs install into the selected virtual environment, or the selected interpreter's user site outside a virtual environment; externally managed installations such as Homebrew additionally require pip's `--break-system-packages` flag, applied only together with `--user`. + ## Side effects, network access, and data Enabling this plugin grants code-execution trust. Concretely, it will: @@ -101,7 +109,7 @@ Enabling this plugin grants code-execution trust. Concretely, it will: - **Execute commands** — `ffmpeg`/`ffprobe` for transcoding and frame extraction, and `skills/web-replicate/scripts/init-webapp.sh`, which runs `npm install` to populate `node_modules`. - **Write files** — `recordings/` (WebM and MP4), `app/` (the scaffolded project), `out/` (`plan.md`, `verify.jsonl`, `report.md`, comparison images under `out/cmp/`), `.v2c/` (plugin root pointer and hook state, including `.v2c/hook_state/interceptions.jsonl`), and a `node_modules` cache under `/tmp/webapp-node-modules` (override with `NM_LOCAL_ROOT`). - **Bind a local port** — `deploy_website` starts a `http.server` on port 8765 or the next free port, bound locally, and stops it when the MCP server process exits. -- **Access the network** — the npm registry when installing template dependencies, and whatever site you ask it to record. +- **Access the network** — the configured Python package index when the doctor installs Python dependencies, the npm registry when installing template dependencies, and whatever site you ask it to record. - **Send no telemetry** and require no API key, token, or account. There are no credentials in this plugin and nothing is uploaded anywhere. ## User configuration diff --git a/plugins/video2code/README_CN.md b/plugins/video2code/README_CN.md index 895863f..16089a7 100644 --- a/plugins/video2code/README_CN.md +++ b/plugins/video2code/README_CN.md @@ -69,7 +69,12 @@ ## Hooks -四个都是 `command` 类型,用 `python3` 执行 [`hooks/`](./hooks) 下的脚本。 +四个都是 `command` 类型。它们和两个 MCP 服务都通过插件自带的 +[`run_python.mjs`](./hooks/run_python.mjs) 启动 Python,不再假设宿主一定提供 +`python3` 命令。Windows 依次尝试 `py -3`、`python`、`python3`,macOS 和 Linux +依次尝试 `python3`、`python`;选中解释器后会原样传递其退出码,并默认把 Python +标准输入输出设为 UTF-8,避免中文 Hook 输出受 Windows 控制台代码页限制。调用方已经 +设置的 `PYTHONUTF8` 或 `PYTHONIOENCODING` 会被保留。 | 事件 | 脚本 | 行为 | | --- | --- | --- | @@ -90,10 +95,12 @@ 不要手工逐项确认,直接跑体检脚本: ```bash -python3 skills/env-setup/scripts/env_doctor.py -python3 skills/env-setup/scripts/env_doctor.py --fix +node hooks/run_python.mjs skills/env-setup/scripts/env_doctor.py +node hooks/run_python.mjs skills/env-setup/scripts/env_doctor.py --fix ``` +启动器会跳过低于 Python 3.10 的解释器。请在与 ZCode 相同的环境中运行体检:GUI 和终端的 `PATH` 可能选中不同解释器。Python 修复会安装到选中的虚拟环境;不在虚拟环境时则安装到该解释器的用户目录。Homebrew 等受外部管理的环境还需要 pip 的 `--break-system-packages` 参数,脚本仅在同时使用 `--user` 时添加它。 + ## 副作用、网络访问与数据 启用本插件等于授予代码执行信任。具体地,它会: @@ -101,7 +108,7 @@ python3 skills/env-setup/scripts/env_doctor.py --fix - **执行命令** —— `ffmpeg`/`ffprobe` 做转码与抽帧;`skills/web-replicate/scripts/init-webapp.sh` 会执行 `npm install` 拉起 `node_modules`。 - **写文件** —— `recordings/`(WebM 与 MP4)、`app/`(脚手架项目)、`out/`(`plan.md`、`verify.jsonl`、`report.md`,以及 `out/cmp/` 下的对照图)、`.v2c/`(插件根路径指针与 hook 状态,含 `.v2c/hook_state/interceptions.jsonl`),以及 `/tmp/webapp-node-modules` 下的 `node_modules` 缓存(可用 `NM_LOCAL_ROOT` 改)。 - **监听本地端口** —— `deploy_website` 在 8765 或之后第一个空闲端口起 `http.server`,只监听本地,MCP 服务进程退出时统一清理。 -- **访问网络** —— 安装模板依赖时访问 npm registry;以及你让它录制的目标网站。 +- **访问网络** —— 体检脚本安装 Python 依赖时访问配置的 Python 包索引;安装模板依赖时访问 npm registry;以及你让它录制的目标网站。 - **不上报任何遥测**,不需要 API key、token 或账号。插件里没有任何凭据,也不会把内容上传到任何地方。 ## 用户配置 diff --git a/plugins/video2code/hooks/check_closeout.py b/plugins/video2code/hooks/check_closeout.py index 65b662e..a9d2ceb 100644 --- a/plugins/video2code/hooks/check_closeout.py +++ b/plugins/video2code/hooks/check_closeout.py @@ -127,8 +127,9 @@ def main() -> None: allow() # 三振/会话预算用尽/已顶回过 → 放行并已记录, 防死循环 block_stop( "复刻契约未闭环, 不能收尾:\n- " + "\n- ".join(res["gaps"]) + - "\n先自己跑审计脚本对账: python3 /skills/video2code/scripts/" - "contract_audit.py (plugin_root 见会话开头横幅, 兜底 cat .v2c/plugin_root), " + "\n先自己跑审计脚本对账: node \"/hooks/run_python.mjs\" " + "\"/skills/video2code/scripts/contract_audit.py\" " + "(plugin_root 见会话开头横幅, 兜底 cat .v2c/plugin_root), " "按缺口逐条补完 (D 项成对证据用 composite_view 的 beats 视频输入一轮即出), " "全绿后再收尾。\n" "若用户本轮要的其实不是这个复刻任务 (契约是本会话早前认领的, 但用户已经转去别的事, " diff --git a/plugins/video2code/hooks/hooks.json b/plugins/video2code/hooks/hooks.json index fff69b8..2012170 100644 --- a/plugins/video2code/hooks/hooks.json +++ b/plugins/video2code/hooks/hooks.json @@ -6,7 +6,7 @@ "hooks": [ { "type": "command", - "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/env_check.py\"", + "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run_python.mjs\" \"${CLAUDE_PLUGIN_ROOT}/hooks/env_check.py\"", "timeout": 30, "statusMessage": "video2code 环境检查…" } @@ -18,12 +18,12 @@ "hooks": [ { "type": "command", - "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/check_video_input.py\"", + "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run_python.mjs\" \"${CLAUDE_PLUGIN_ROOT}/hooks/check_video_input.py\"", "timeout": 15 }, { "type": "command", - "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/check_url_input.py\"", + "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run_python.mjs\" \"${CLAUDE_PLUGIN_ROOT}/hooks/check_url_input.py\"", "timeout": 10 } ] @@ -35,7 +35,7 @@ "hooks": [ { "type": "command", - "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/check_plan_first.py\"", + "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run_python.mjs\" \"${CLAUDE_PLUGIN_ROOT}/hooks/check_plan_first.py\"", "timeout": 10 } ] @@ -46,7 +46,7 @@ "hooks": [ { "type": "command", - "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/check_closeout.py\"", + "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/run_python.mjs\" \"${CLAUDE_PLUGIN_ROOT}/hooks/check_closeout.py\"", "timeout": 15 } ] diff --git a/plugins/video2code/hooks/run_python.mjs b/plugins/video2code/hooks/run_python.mjs new file mode 100644 index 0000000..96fde44 --- /dev/null +++ b/plugins/video2code/hooks/run_python.mjs @@ -0,0 +1,63 @@ +#!/usr/bin/env node +/** Run a plugin Python entrypoint with platform-appropriate interpreter names. */ + +import { spawnSync } from "node:child_process"; + +const [script, ...scriptArgs] = process.argv.slice(2); + +if (!script) { + process.stderr.write("Usage: run_python.mjs