产品视频(及其周边资产),端到端
您正在从实时网页制作一个可交付的产品资产——一段精良的视频、一组精确尺寸的静态图片,或两者兼备——使用 vos CLI。一切都是数据:您编写流程脚本,CLI 录制并规划,您调整 JSON,然后重新渲染。永远不要为了调整节奏或缩放而重新录制——编辑 doc.json,然后重新渲染即可。 质量标准:references/taste.md——遵循其质量循环,并使用多模态判断静态图片。
设置
npm i -D @vosjs/cli
一条命令,一个包:@vosjs/cli 是开源 (MIT) 的 vos 二进制文件,包含引擎指令、此处使用的录制管道(录制/规划/帧/渲染屏幕录制)以及 vos.so 平台指令。(在 0.9 版本之前,录制管道作为 @vosso/vos-plugin 单独分发;仍列出该依赖的项目可以删除它。)要求:
- 一个 Chromium:系统 Chrome 会被自动找到;否则运行
npx playwright install chromium或将VOS_BROWSER_PATH指向一个。退出码 3 表示未找到浏览器。 - 渲染时网络可用:渲染页面从 esm.sh 加载 three.js 和 mediabunny。录制还需要访问目标 URL。完全离线的沙箱无法渲染——请这样说明,而不是什么都不输出。
约定:日志 → stderr,结果 → stdout;--json 输出 NDJSON,以 {"event":"done",…} 结束;退出码:0 正常 / 1 错误 / 2 使用或严格失败 / 3 无浏览器 / 4 录制器遇到登录页面而不是目标页面(缺少或过期的 SESSION,而非脚本错误:参见 references/sessions.md)。vos <verb> --help 打印该命令的标志(@vosjs/cli 0.41.1 及更高版本)。
步骤 0——选择目标(这决定了所有事情)
| 目标 | 视口 | 输出 | 额外 |
|---|---|---|---|
| 登陆页面片段(英雄区/区域嵌入) | 2560×1440(素材原生 2K) | webm → VP9 重新编码 + 海报图 | 静音循环;参见 references/destinations.md |
| 发布视频(Product Hunt、社交媒体、商店推广) | 2560×1440 或 1280×720 | mp4(--format mp4,需要系统 Chrome) | 通过 doc.audio 添加背景音乐 |
| 发布工具包静态图片(商店截图、磁贴、OG 图片) | 根据资产调整大小 | 从同一段录制中通过 frames --frame <t> --size WxH 输出 PNG | 一次录制 → 所有资产 |
| 快速演示(问题、PR、聊天) | 1280×720 | webm,默认设置 | 速度优先于精致;仅在此处可以接受草稿 |
各频道的尺寸和字节预算:references/destinations.md。
核心循环(所有目标)
-
使用您自己的工具探索目标页面(抓取 HTML / Playwright)。识别出讲述一个故事的 3-6 个时刻。收集稳定的选择器(
a[href='…']、id、角色——不要用 nth-child 链)。 像布置场景一样准备内容:脚本必须使产品处于一个令人自豪的截图会展示的状态——标签已输入,数据看起来真实,功能处于故事中期。一个空画布录制很快,但演示不出任何内容,后续的任何合成都无法挽救它。需要登录?在脚本开始前搞定会话。 一个没有会话的录制器会录制到限制页面(登录页面,或网站向陌生人展示的公共页面),唯一的症状是跳过的选择器。按照
references/sessions.md中的阶梯从上到下执行,并在第一个有效的阶梯处停止:无限制页面(演示模式、关闭了认证的本地服务器)→ 从项目已有的测试认证中铸造一个会话(playwright/.auth、auth.setup.ts、种子脚本:先查找,再询问任何人)→ 在actions.json中使用setup在镜头外登录(密码来自{ "env": "NAME" },绝不能是字面量)→ 人类登录一次(vos session open <url> --name <app>,然后在录制时加上--session <app>)→ 人类使用扩展程序从镜头列表中录制vos actions script actions.json输出的内容,然后您剪辑它。每个阶梯都以setup、一个用于--storage-state的状态文件、或一个用于--session的命名会话结束。绝不能输入或接受生产环境密码,将状态文件保留在录制目录和 git 之外,并使用演示账户录制:视频中展示的是该账户显示的内容。已经在 agent-browser 中验证过该功能? 保留那次操作过程,跳过第二个脚本。agent-browser 的
--json结果并未说明执行了什么操作(scroll返回{scrolled:true}),因此封装每个调用,使命令与其结果并排放置,然后转换:ab() { agent-browser "$@" --json | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const r=JSON.parse(s);process.stdout.write(JSON.stringify({command:process.argv.slice(1),...r})+"\n")})' -- "$@" >> steps.jsonl; } ab open https://target.example; ab snapshot -i -u; ab click @e27 ab wait 800; ab snapshot -i # 页面已更改:引用重新编号 ab fill @e2 query; ab press Enter vos actions from-agent-browser steps.jsonl --out actions.json(作为一次
agent-browser batch … --json > steps.jsonl运行的完整路径已经具有该结构)。日志是steps.jsonl,放在您所在的目录中,因此从一个目录开始操作。引用通过它们之前的最后一个snapshot -i解析,并且在导航后重新编号,在对话框内也会重新编号:在发生任何改变页面的操作后重新快照,并在命名引用之前读取它们(-u为链接赋予其 href)。点击控件,不要使用其快捷键:⌘K 为人类打开对话框,但一次按键是录制器无法重放的一个步骤。录制器无法遵循的任何内容(快捷键、拖拽、第二次open)都会在输出中被命名,而不会被丢弃:阅读这些说明,手动编写这些步骤,然后录制。 -
编写
actions.json:{ "url": "https://target.example", "viewport": { "width": 1280, "height": 720 }, "steps": [ { "do": "wait", "ms": 800 }, { "do": "hover", "selector": "a[href='/pricing']", "ms": 700 }, { "do": "click", "selector": "#cta" }, { "do": "wait", "ms": 1500 }, { "do": "scroll", "dy": 400 }, { "do": "move", "x": 640, "y": 320 }, { "do": "wait", "ms": 900 } ] }动词:
waithoverclicktypescrollmovedrag(type={do:'type', selector, text, delayMs?, ms?, focus?}:它会点击字段,然后输入text;focus:false会输入到已经聚焦的元素中,用于提交 Enter) (drag = 真实编辑:{do:'drag', selector|x,y, tx, ty, ms}—— 滑动范围输入框、拖拽 canvas 元素、移动时间线片段)。节奏就是缩放计划:打开的wait ≥700ms;悬停在重要元素上 700–900ms(停留会成为缩放);点击的ms是阅读时间,从页面最后一次更改开始计时(@vosjs/cli0.47 及更高版本;录制器自行处理稳定),因此将其大小作为一个节拍:导航后大约 1000ms,控件后 600ms,永远不要为慢页面填充时间;最终保持稳定。将光标从悬停触发的菜单移开(请参见taste.md中的流程规则)。使用vos validate actions.json检查。然后排练它(
@vosjs/cli0.39 及更高版本):vos record --actions actions.json --out take --dry-run。每个步骤都针对真实页面按顺序运行,因为后面的选择器通常只有在之前点击后才存在,但不捕获任何内容,也不写入任何内容:丢失的选择器会在几秒钟内被命名(退出码 2),而不是在实时录制和编码之后;如果--out中已有录制,则会保留其素材。它会打印每个步骤在捕获像素中的矩形,这也是固定图层所读取的内容。在每次脚本编辑后重新排练,并且只录制通过排练的脚本。已登录产品的排练方式相同:--storage-state和--browser-arg=也适用于它,并且排练会通过列出帧暴露的内容(地址、密钥、卡片)来结束。在录制前使用mask隐藏这些内容:请参见references/sessions.md。 -
录制:
vos record --actions actions.json --out take --strict --json始终使用--strict:跳过选择器 / networkidle 超时 → 退出码 2,并在完成事件中包含skipped[]。跳过意味着流程已损坏——修复它,永远不要绕过它继续。录制会自动编码和自动规划。 (vos create --actions actions.json out.webm --strict是一次性录制+渲染命令——适合快速初版,但此技能的工作流程在渲染前审查帧,因此这里首选单独的命令。) -
微调
doc.json(JSON Schema 包含在@vosjs/clinpm 包中:schema/doc.schema.json):zoom:[{in, out, level, cx, cy, source}],source单位是秒;level 范围 1.4–2.8;cx/cy是归一化的 [0..1](0.5,0.5 = 中心)——不是像素;在你修改的时间段上设置"source": "manual"(可抵御重新规划)。segments(裁剪)·speed(rate0.1–16)·frame.*·cursor。tilt:[{in, out, rx, ry, source}],source单位是秒——3D 卡片在激活时向某个姿态倾斜,在动作之间返回静止。单位为度(±5..18 显得高级):+rx = 顶部边缘更近,+ry = 左侧边缘更近(向右侧焦点倾斜 = 负的ry)。持续时间 ≥ 0.8s;与缩放时刻配对(相同的 in/out 将运动链在一起),每约 5s 节拍一次姿态变化。在你修改的时间段上设置"source": "manual";tiltStyle: "subtle"|"medium"|"strong"记录自动魔棒。frame.backgroundMedia:卡片后面的视频循环 / 图像——{"kind":"video","key":"/bg.webm","duration":10,"dim":0.2}。key= 放入录制目录的文件("/bg.webm")或媒体 URL;视频需要duration(基于输出锚定的模循环);dim0..1 遮光布。氛围,而非主题——在密集的 UI 后面调暗它。audio:基于输出锚定的剪辑;key可以是放入录制目录的文件("/music.mp3");gain/fades/loop。在完整渲染时混流(Opus/AAC);--range保持静音;强制执行单次飞行。- export:
{"resolution": "720p|1080p|2k|4k", "fps": 30}——永远不要超过素材分辨率(验证会发出警告)。 然后运行vos validate take --json——lint 必须通过。
-
在渲染前查看(taste.md 质量循环):
vos frames take --at-zooms --times 0,25%,50%,75%,100% --json→ 对照 taste.md 评判每一帧,缩放顶点处最严格。- 迭代:编辑 doc.json →
vos render take check.webm --range a..b --draft(秒为单位,半分辨率——绝不要发布草稿)→ 重新评估已更改区域。 - 想尝试演示?使用标志,而不是草稿脚本。
render/frames接受 doc 覆盖项——--set <path>=<value>(可重复;JSON 或字符串)、--frame <macos|windows|minimal|none>(渲染)、--background <url>——这些会在内存中修补 doc(doc.json 保持原样)并受 lint 检查限制。因此,vos frames take --frame 2.0 --set frame.browserBar.kind=mac-light --set tilt[0].rx=8可以在不修改文件的情况下预览一个带边框和倾斜的卡片。
-
最终渲染:
vos render take out.webm --json(或--format mp4)。在宣布完成前,根据 taste.md 重新评估最终结果(frames --at-zooms)。渲染是确定性的——只有您的编辑会改变输出。
6b. 人工审核轮次(当要求涉及此步骤时):vos open take 会将录制提供到工作室——您的 doc.json 编辑会完整保留。