用命令行发布 widget
用 VizChat CLI 登录、本地预览带讲稿语音的 widget 演示,并把它发布到 Marketplace——全程在终端完成。
如果你在本地开发 widget——用自己的编辑器、自己的工具链——vizchat CLI 能把它从你机器上的一个文件夹变成 Marketplace 上的发布条目。本指南覆盖你需要的命令:auth login、widget dev、widget tts、widget publish,以及云端副本在你开发期间变了时用的 widget merge。
前提:Node.js、包管理器和 VizChat 账号。
创建项目
npm install -g vizchat
vizchat auth login
vizchat widget create my-widget --mount inline选择挂载形态:inline 在 host React 树里渲染组件,nested 在独立 iframe 里操作 DOM。命令在云端工作区创建 widget,并同步完整的本地包,包括 schema、视图、演示、双语文案和测试。
已经有 widget 包了?那只需确认 devDependencies 里含 vizchat,且 name 形如 @<你的用户名>/<widget-slug>。
cd my-widget && pnpm install用 pnpm 11 及以上安装时,依赖里有没表过态的构建脚本,安装会直接报 ERR_PNPM_IGNORED_BUILDS 停下——这里是 vizchat 用到的 esbuild。widget 不需要 esbuild 跑它的构建脚本,在 package.json 旁的 pnpm-workspace.yaml 里写明不跑即可:
allowBuilds:
esbuild: false也可以按提示跑 pnpm approve-builds 选它。npm 不拦这一项。
CLI 在项目内以 pnpm exec vizchat 调用;想在 widget 包之外也能用,就全局安装(npm install -g vizchat)。下文直接写 vizchat。
作用于 widget 的命令会从当前目录向上找 widget 包根;想指明就加 --root <dir>。
vizchat widget create 生成的包同时在云端建好这个 widget 的草稿、克隆到本地文件夹。这样得到的文件夹由 vizchat workspace sync 与云端草稿保持一致(见本地工作区命令行)。
登录
vizchat auth loginCLI 会打印一个短码和一条浏览器链接。打开链接、核对短码一致后批准——终端会自动拿到凭证。批准页列出 CLI 申请的权限:读、写与发布;不想让这台机器发布,可以取消勾选「发布」(之后需要时重新登录即可补上)。凭证存放在 ~/.config/vizchat/credentials.json,按签发它的服务器隔离,会自动续期;30 天没用过才需要重新登录。
vizchat auth whoami 查看当前登录身份与授予的权限;vizchat auth logout 在服务端撤销这次登录并删除本地凭证。你也可以随时在 Web 应用设置 → 已连接的应用与设备里撤销它。
本地预览
vizchat widget dev它会启动本地预览服务并监听源码。演示标签播放你的 demo.stb storyboard;交互标签把 widget 挂进一个可点击、可编辑的实时 dashboard。源码改动会自动重编译并刷新。
合成讲稿语音
demo storyboard 里 markedText 的讲稿可以合成为语音,在本地预览中播放,并随发布一起交付:
vizchat widget tts合成消耗你账户的积分,且只合成文本有变化的页——内容没变时重跑不产生费用。音频存在包内的 .vizchat/local/demo-derived/(已被 git 忽略,不会编进 widget)。widget publish 会把它随包一起上传,发布时直接复用,不再重新合成、也不再扣费。当有页面缺音频时,预览页也会显示一键合成提示条。
每一页讲稿都要有语音。有页合成失败时,widget tts 会保留已成功的页,并以错误退出、列出失败的页——再跑一次只重试这些页(已合成的页不会重复扣费),也可以直接发布,由服务端补合成缺的页。有讲稿的页,页 id 会成为语音文件路径的一段,因此不能含 /、.. 或控制字符,且不超过 128 字符;不满足时 widget tts 与 widget publish 都会拒绝。
发布
vizchat widget publishCLI 先在本地构建(明显的错误不出你的机器),然后让组件的云端草稿与你的文件夹一致,再由服务端编译、校验并发布。服务端是权威:它从源码重新构建,并把你的 demo 语音烘焙进发布版本——本地已合成的音频直接复用,不重复计费。有 demo 的组件,市场封面也在这次发布中从 demo 渲染出来。发布在服务端以排队的后台任务进行;CLI 会等它结束(排队时提示 waiting in the publish queue…)再报告新版本号。发布前它还会扫描一遍源码里的疑似密钥(见下文「密钥扫描」)。
包目录里的文件都会推送,只排除根目录下的 node_modules/、dist/、dist.prev/、.git/、.vizchat/(以及构建产出的 manifest.json)。本地演示语音(.vizchat/local/demo-derived/)不作为包源码推送,而是如上所述单独随请求带上。整个包的文件最多约 18 MB;CLI 在发出任何请求之前就会检查。
在浏览器里确认
发布撤不回。用 vizchat auth login 登录的 CLI 发布之前,会停下并打印一个确认链接:在浏览器里打开它(用同一个账号登录),
核对要发布的组件、以及这一版的源码会不会公开,点确认,再原样重跑同一条命令。确认只对这一次、这一份请求有效。确认链接在服务端的检查(含下文的密钥扫描)
都通过之后才出现:会被拒绝的请求不会先让你确认。CI 访问令牌与可信发布在设置页签发 / 登记时已经授权,不需要确认。
云端草稿也会动
推送只在云端草稿仍是你上次推送的样子时才成功——与 git push 同一条规则。这期间如果有人改了草稿(在网页编辑器里,或由 AI),widget publish 不会覆盖它,而是逐个文件比较:
- 只有你这边改了:保留;
- 只有云端改了:拉进你的文件夹;
- 两边改成了同样的内容:无事发生;
- 两边改得不一样:写进
.vizchat/merge/<id>/{base,local,remote}/<路径>,命令以merge_pending(退出码 10)停下。
遇到待合并,把 remote/ 里的云端版本合进你的工作目录(自己来,或让你的编程助手来),然后:
vizchat widget merge --continue # 构建、推送合好的文件、清理
vizchat widget merge --abort # 放弃本地合并不带参数的 vizchat widget merge 会列出当前待合并的内容。合并未完成时 widget publish 拒绝执行。
「上次推送」怎么认,取决于这个文件夹怎么来的。 由 vizchat widget create 或 vizchat workspace clone 得到的是同步过的克隆目录:widget publish 先跑一次 workspace sync,基线由同步引擎维护。git clone 或 CI 检出的目录没有同步状态,CLI 会在每次推送成功后把基线写进 .vizchat/base.json。**请把这个文件提交进 git。**没有它,全新检出的目录分不清「我新增的」和「云端删掉的」,每个不一样的文件都会被当成两边都改了,要你去合并。忘了提交较新的那份也不会出错:云端多出来的改动本来就在你的文件夹里,按「两边一致」处理。
版本
每次发布都会产生一个新版本,发布后的版本不再改动。版本号不用你选:组件的状态 schema 和操作没变,新版本就标为修复;变了就标为功能更新。如果新版本读不懂旧版本存下的数据,用 --breaking 明确声明:
vizchat widget publish --breaking新版本不会自动进入别人已经做好的文档:用到你组件的演示与看板都固定在当时的版本上,主人打开时看到「有更新」的提示,自己决定要不要换。数据格式变了的版本(功能更新)发布后,网页的发布确认里会列出「与上一版不兼容的地方」:有多少个在用旧版本的文件、你的 schema 里哪些字段对不上——只有这些汇总,看不到任何一份文件的内容。这份提示不会拦住发布;对应的文件主人更新时也会看到。
发布即公开
发布出去的组件立刻就在 Marketplace 上可见,没有「先发布、再上架」两步。你唯一要另外决定的是要不要连源码一起公开。这件事写在 package.json 里,不是命令参数,按版本生效:
{
"license": "MIT",
"vizchat": {
"mount": "inline",
"displayName": "My Widget",
"sizeConfig": { "width": 400, "height": 300 },
"openSource": true
}
}不写 "openSource": true 就不公开(缺省);此时 license 不写或写 "UNLICENSED"。写了它,license 必须是一个开源许可证——两者对不上,构建直接报错。发布结果会告诉你刚发的这一版是否开源("sourceOpen": true,输出里带 (source open))。
已开源的版本收不回——已经被看到的东西收不回来。之后的版本可以去掉声明重新闭源:从那以后 Marketplace 不再提供源码下载和「导入到我的组件」,但已开源的版本仍按它的许可证可用。所以先干跑一次看看会发生什么:
vizchat widget publish --dry-run干跑会在本地构建、列出将要推送的文件,然后停下——不发任何网络请求。
想让组件不再出现在 Marketplace,用网页端的「下架」;那是可逆的,与源码是否公开无关。
密钥扫描
每次发布都会扫一遍源码里的疑似密钥:源码不公开时,编译出的组件也是公开的,写进代码的密钥一样会泄露。命中时什么都不会发出去。 如果命中的是有意为之的内容(例如演示用的 API key 占位符),确认无误后重跑:
vizchat widget publish --acknowledge-secrets回路里的其他命令
| 命令 | 做什么 |
|---|---|
vizchat widget build | 本地构建(产出 dist/ 与 manifest.json) |
vizchat widget test | 跑组件自己的测试 |
vizchat widget lint | 不需要渲染的静态检查:schema 与依赖声明 |
vizchat widget merge | 查看、完成(--continue)或放弃(--abort)待合并 |
在脚本与 CI 里使用
每个命令的 stdout 末行恒为一行 JSON,进度与提示走 stderr,因此可以直接 | tail -1 | jq。
退出码是细分的,可据此决定下一步:
| 码 | 含义 | 下一步 |
|---|---|---|
0 | 成功 | 继续 |
1 | 意外失败(internal) | 告诉人 |
2 | 用法 / 参数错(invalid_argument),或 CLI 太旧(client_outdated) | 改命令 / 升级 |
3 | 未登录或凭证失效(auth_required) | 跑 auth login |
4 | 本地校验 / 构建 / 测试失败(invalid_content) | 改代码 |
5 | 需要确认(confirmation_required) | 打开打印的确认链接确认后原样重跑;密钥扫描命中则带 --acknowledge-secrets、其它命令带 --yes 重跑 |
6 | 远端 / 网络错误(unavailable) | 可重试 |
7 | 无权操作(permission_denied) | 确认目标属于你 |
8 | 找不到(not_found) | 核对名称 |
9 | 冲突(conflict) | 重新读取最新状态,或换个名字 |
10 | 有待合并的本地合并(merge_pending) | 合好后 widget merge --continue |
11 | 积分或存储不足(quota_exceeded) | 充值或腾空间,不要重试 |
末行 JSON 里还带小写的 code 与给出下一步的 hint;vizchat --help 会打印这张表。
CI 里不用 auth login,两种凭证任选:
- 访问令牌:在设置 → 已连接的应用与设备里签发,选定权限、有效期与能推送的 widget,存进 CI 的密钥并设为环境变量
VIZCHAT_TOKEN。设了它 CLI 就用它,不读也不写本地凭证文件。 - 可信发布(GitHub Actions,不用存任何密钥):在同一页把仓库、workflow 文件(可选 environment 与分支)登记为某个 widget 的可信发布方,给 job 开
permissions: id-token: write,发布前先跑vizchat auth exchange——它用 GitHub 签发的身份令牌换一个 15 分钟的凭证,只能推送、发布登记的那个 widget。
会扣费或有不可逆副作用的命令在非交互环境下不会默认放行:widget tts 需要显式 --yes(或用 --dry-run 只看报价)。
疑难排查
- 刚登录完就提示「未登录」 —— 凭证按服务器隔离。如果你是通过
VIZCHAT_API_URL登录到非默认服务器的,之后每个命令(包括widget dev)都要带同一个环境变量。 - 预览提示缺音频但你已经合成过 —— 预览页在加载时读取本地音频,在终端里跑完
widget tts后刷新一下预览页即可。合成之后又改过讲稿文字的页,在重新合成之前保持静音。 widget publish拒绝作者名 ——package.jsonname里的 scope 必须与你的 VizChat 用户名完全一致。