@pippit-dev/cli 1.0.23 → 1.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,50 @@
1
+ # Canvas:个人画布与命令发现
2
+
3
+ 用于小云雀个人漫剧画布操作,不适用于其他产品的画布或团队空间。与 [生图](generate-image.md)、[生视频](generate-video.md) 区分:修改画布文档不等于生成媒体,独立生成也不会自动写回指定节点。
4
+
5
+ ## 两个入口
6
+
7
+ 先运行 `node "{baseDir}/scripts/ensure-cli.js" --canvas`。返回:
8
+
9
+ ```json
10
+ {"cli_path":"/absolute/package/bin/pippit-tool-cli","version":"实际版本","canvas_entry":"/absolute/package/scripts/run.js"}
11
+ ```
12
+
13
+ - 原生资产命令:`pippit-tool-cli canvas ...`,命令名替换为 `cli_path`。
14
+ - SDK 语义命令:`node "CANVAS_ENTRY" canvas command ...`,`CANVAS_ENTRY` 替换为返回的 `canvas_entry`。不要把 Go 二进制的帮助输出当作语义命令可执行的证明。
15
+ - 切换 shell 时仍使用保存的绝对路径;Windows PowerShell 原生命令用 `& "CLI_PATH" ...`,Node 命令用 `node "CANVAS_ENTRY" ...`。授权复用 [CLI 凭据](auth.md)。
16
+
17
+ ## 按用户意图发现操作
18
+
19
+ 先读取精简目录,仅展开要用的命令;以下名称用于定位,以当前安装版本返回的目录为准。
20
+
21
+ | 操作 | 入口或可定位的语义命令 | 要点 |
22
+ | --- | --- | --- |
23
+ | 创建画布、查资产、上传素材、分配 ID、提交事务 | `canvas create/get/upload/allocate/apply` | 见 [原生资产命令](canvas-assets.md) |
24
+ | 读取画布和资产 | `get_snapshot`、`get_asset`、`get_permissions` | 取得真实节点/资产 ID,不用名称猜 ID |
25
+ | 创建业务节点 | `create_biz_node` | 支持类型和初始字段用 `--node-kind` 查看;不要手拼业务节点结构 |
26
+ | 节点位置、大小、样式、布局、分组 | `move_nodes`、`resize_node`、`set_node_style`、`align_nodes`、`arrange_nodes`、`group_nodes` 等 | 先查询目标和归属;删除与重排仅限用户请求范围 |
27
+ | 连线和画布配置 | `create_edge`、`reconnect_edge`、`delete_edges`、`set_title`、`set_cover` 等 | 连线两端来自当前画布查询 |
28
+ | 角色与场景资料 | `xyq.role.updateDescription/updateAppearance/updateSourceInfos`、`xyq.scene.updateDescription` | 用完整命令名分别 describe,遵守具体字段 |
29
+ | 图片/视频节点提示词和已有引用 | `xyq.generation.update_prompt` | 先读 `guide prompt-references`;保留其他生成参数,不触发生成 |
30
+ | 3D 导演台对象、摄像机、关键帧、动作 | `xyq.scene3d.query` / `xyq.scene3d.apply` | 先读 `guide scene3d` 和 `guide time` |
31
+ | 多轨草稿、轨道、片段、输出尺寸 | `xyq.timeline.query` / `xyq.timeline.apply` | 先读 `guide timeline` 和 `guide time`,使用最新 `expectedRevision` |
32
+ | 检查点创建、列表、比较、恢复 | `create_checkpoint`、`list_checkpoints`、`compare_checkpoint`、`restore_checkpoint` | 需要网页登录的 credential_scope;本地检查点按账号与画布隔离,不是跨机器备份 |
33
+
34
+ ```bash
35
+ node "CANVAS_ENTRY" canvas command list
36
+ node "CANVAS_ENTRY" canvas command describe create_biz_node --node-kind role
37
+ node "CANVAS_ENTRY" canvas command describe xyq.timeline.apply --operation set_output_size
38
+ node "CANVAS_ENTRY" canvas command schema xyq.timeline.apply
39
+ node "CANVAS_ENTRY" canvas command guide
40
+ ```
41
+
42
+ `list --category 类别` 缩小范围;`describe --path schema.path` 展开字段。`describe` 是摘要视图,嵌套分支不完整;构造复杂输入时用 `schema 命令名` 取得完整契约。不要默认导出所有 schema。需要的命令不存在时报告当前版本的能力限制,不调用猜测的命令。
43
+
44
+ ## 边界与易混淆点
45
+
46
+ - `create_biz_node` 可建立文字、图片、视频、音频、角色、场景、3D、多轨等业务节点;具体 `nodeKind` 以 schema 为准。创建节点本身不执行生成。
47
+ - 提示词标签只解析已有画布节点及目标草稿中的引用。只有上传得到的裸 ID 不能自动加入草稿;无法解析时停止,不能用临时 URL 或虚构引用替代。普通提示词更新不等于故事板脚本编辑。
48
+ - 目前没有公开的故事板镜头增删排序、脚本保存、指定镜头生成领域命令;`guide storyboard` 是说明,不是执行能力。底层补丁不能替代这些业务流程。
49
+ - 3D 和多轨命令编辑文档,不提供截图、渲染或最终视频导出。多轨时间为整数微秒,3D 动画时间区分帧和源动画秒;先查字段 schema。
50
+ - 编辑后按 [画布查询、编辑与验证](../workflows/canvas-edit.md) 回读确认;无需使用媒体异步查询来判断画布写入成功。只有实际取得图片/视频文件时才进入媒体交付标准。
@@ -0,0 +1,13 @@
1
+ # erase-video-subtitle:擦字幕
2
+
3
+ 用于去除已有视频字幕;不代表支持删除任意水印、标志或画面对象。
4
+
5
+ 必填参数 `--video` 接收一个本地视频路径,命令内部上传;没有可用视频文件时先补齐输入。
6
+
7
+ ```bash
8
+ pippit-tool-cli erase-video-subtitle --video "/path/to/source.mp4"
9
+ ```
10
+
11
+ 成功返回 `thread_id`、`run_id`、`web_thread_link`,继续 [异步结果与媒体交付](../workflows/async-delivery.md)。失败时报告原因,不把其他生成命令当作字幕处理的自动降级方案。
12
+
13
+ 用户还明确要求超分时,参考 [擦字幕后超分](../examples/video-process-chain.md),用第一步下载得到的实际视频路径衔接第二步。
@@ -0,0 +1,32 @@
1
+ # generate-image:生图与参考图编辑
2
+
3
+ 适用于普通文生图、指定模型生图和基于参考图片的编辑。目标是生成视频时读取 [生视频](generate-video.md)。
4
+
5
+ ## 输入与参数
6
+
7
+ | 参数 | 必填 | 规则 |
8
+ | --- | --- | --- |
9
+ | `--prompt` | 是 | 用户原始描述,不能全为空白 |
10
+ | `--model` | 是 | 用户选择的图片模型;缺少时先询问 |
11
+ | `--image` | 否 | 本地参考图路径,多张图重复此参数,保留用户指定的角色与顺序 |
12
+ | `--ratio` | 否 | 整数枚举,按下表转换明确的比例要求 |
13
+ | `--resolution` | 否 | 仅 `seedream_5.0_pro` 支持 `1K`、`2K`、`4K` 选项 |
14
+ | `--generate-image-count` | 否 | 用户指定的生成数量 |
15
+
16
+ 比例映射:`0=原始比例/自动`、`2=16:9`、`13=21:9`、`3=9:16`、`4=4:3`、`5=3:4`、`6=1:1`。此命令接收整数,不传 `--ratio "16:9"`。用户未给比例时省略;不明确的比例先确认,不猜枚举。
17
+
18
+ 当前 CLI 帮助列出的模型包括 `seedream_5.0_pro`、`seedream_5.0`、`seedream_4.3`、`nova2`、`seedream_4.5`、`seedream_4.1`、`seedream_4`。用于提示选择,实际支持情况以当前帮助和服务端为准,不在 Skill 中增加模型白名单校验。
19
+
20
+ 本地图片后缀支持 `.jpg/.jpeg/.png/.gif/.bmp/.webp/.svg`。参考图由命令内部上传,不需要自行获取资产 ID。
21
+
22
+ ## 最小调用
23
+
24
+ ```bash
25
+ pippit-tool-cli generate-image --prompt "用户原始描述" --model IMAGE_MODEL
26
+ ```
27
+
28
+ 只追加用户已提供的可选参数。多图编辑见 [参考图编辑示例](../examples/image-edit.md)。
29
+
30
+ ## 返回与处理
31
+
32
+ 成功返回 JSON 中的 `thread_id`、`run_id`、`web_thread_link`;随后执行 [异步结果与媒体交付](../workflows/async-delivery.md)。参数错误、上传失败或服务端拒绝时停止并说明原因,不切换模型或重新提交。仅在已明确失败、问题已修正且原授权仍适用时重试;提交结果不确定时避免重复收费。
@@ -0,0 +1,33 @@
1
+ # generate-video:生视频
2
+
3
+ 适用于文生视频、参考图/视频/音频生成新视频以及首尾帧生成。仅处理已有视频的清晰度或字幕时,分别使用 [超分](video-super-resolution.md)、[擦字幕](erase-video-subtitle.md)。
4
+
5
+ ## 输入与参数
6
+
7
+ | 参数 | 必填 | 规则 |
8
+ | --- | --- | --- |
9
+ | `--prompt` | 是 | 用户原始描述,不能全为空白 |
10
+ | `--model` | 否 | 用户指定的模型;未提供时省略,由服务端处理默认配置 |
11
+ | `--image` | 否 | 本地图片路径,重复参数,最多 9 张 |
12
+ | `--video` | 否 | 本地参考视频路径,重复参数,最多 3 个 |
13
+ | `--audio` | 否 | 本地 `.mp3/.wav` 音频路径,重复参数,最多 3 个 |
14
+ | `--duration` | 否 | 整数秒;用户只给时长范围时先确认具体秒数 |
15
+ | `--ratio` | 否 | 比例字符串,如 `9:16`、`16:9`、`3:4`、`4:3`;不转换为生图枚举 |
16
+ | `--resolution` | 否 | 用户指定值,如 `720p`、`1080p` |
17
+ | `--generate-type` | 否 | 首尾帧任务传 `1`;其他显式值交服务端处理 |
18
+
19
+ 普通用户模型为 `Seedance_2.0_mini_lite`;VIP 模型包括 `seedance2.0_vision`、`seedance2.0_fast_vision`、`Seedance_2.0_mini`、`Seedance_2.5`。该列表仅用于选择提示,以当前帮助和服务端为准,不自行新增模型或分辨率组合校验。
20
+
21
+ 本地图片支持 `.jpg/.jpeg/.png/.gif/.bmp/.webp/.svg`;视频支持 `.mp4/.avi/.mov/.wmv/.flv/.webm/.mkv/.m4v`。CLI 内部上传参考素材。
22
+
23
+ ## 最小调用
24
+
25
+ ```bash
26
+ pippit-tool-cli generate-video --prompt "用户原始描述"
27
+ ```
28
+
29
+ 首尾帧场景:明确两张图片的角色,按首帧、尾帧顺序传两次 `--image`,固定传 `--generate-type 1`。缺少图片、角色不清或无法确定原始描述时先询问。完整示例见 [首尾帧生视频](../examples/first-last-frame.md)。
30
+
31
+ ## 返回与处理
32
+
33
+ 成功返回 `thread_id`、`run_id`、`web_thread_link`,继续 [异步结果与媒体交付](../workflows/async-delivery.md)。素材、权限或参数失败时说明原因,不自动降低用户指定的模型、分辨率或删减素材。无法确认提交是否成功时,不重复生成。
@@ -0,0 +1,17 @@
1
+ # get-credit-balance:个人积分余额
2
+
3
+ 用于查询当前 CLI 凭据所属用户的个人有效积分余额。无需用户 ID 或任务 ID,不创建生成任务、不轮询,也不需要消耗积分的确认。
4
+
5
+ ```bash
6
+ pippit-tool-cli get-credit-balance
7
+ ```
8
+
9
+ 成功输出示例:
10
+
11
+ ```json
12
+ {"total_remain_amount":"123"}
13
+ ```
14
+
15
+ 读取字符串 `total_remain_amount` 展示余额,`"0"` 是有效零余额。失败或缺少字段时报告查询失败,不能当作余额为零。
16
+
17
+ 排查请求时使用 `pippit-tool-cli get-credit-balance --with-log-id`,保留返回的 `log_id`。该命令不提供积分明细、到期时间或生成任务费用估算。鉴权失败按 [授权说明](auth.md) 处理。
@@ -0,0 +1,47 @@
1
+ # query-result:查询异步结果并下载
2
+
3
+ 用于生成和视频处理命令返回的任务,也用于用户要求查询的已有任务。三个参数均必填:
4
+
5
+ | 参数 | 含义 |
6
+ | --- | --- |
7
+ | `--thread-id` | 原任务的 `thread_id` |
8
+ | `--run-id` | 要查询的那一次运行的 `run_id`,不得混用其他运行 |
9
+ | `--download-dir` | 本地输出目录,命令在任务成功时自动下载产物 |
10
+
11
+ 用户未指定目录时使用 `./xyq_output`。已知历史任务但缺少任一 ID 时,从当前上下文获取,无法确定则询问;不要通过重新生成来补 ID。
12
+
13
+ ```bash
14
+ pippit-tool-cli query-result --thread-id THREAD_ID --run-id RUN_ID --download-dir "./xyq_output"
15
+ ```
16
+
17
+ ## 输出契约
18
+
19
+ stdout 是 JSON;命令可能将错误编码进 JSON 并以退出码 0 返回,因此必须先检查 `error_message`。
20
+
21
+ | 字段 | 含义 |
22
+ | --- | --- |
23
+ | `completed` | 是否结束;失败时也可能为 `true`,不等于成功 |
24
+ | `error_message` | 非空即错误,不能因为 `completed=false` 而忽略 |
25
+ | `thread_id` / `run_id` | 对应的查询任务 |
26
+ | `images[]` / `videos[]` | 成功后获取的媒体,每项含 `download_url`、`output_path` |
27
+
28
+ 成功示例(ID、URL 和文件名仅为示意):
29
+
30
+ ```json
31
+ {
32
+ "completed": true,
33
+ "thread_id": "THREAD_ID",
34
+ "run_id": "RUN_ID",
35
+ "error_message": "",
36
+ "images": [{"download_url": "https://example.com/image.jpeg", "output_path": "./xyq_output/asset.jpeg"}],
37
+ "videos": []
38
+ }
39
+ ```
40
+
41
+ 任务尚未完成时通常返回 `completed=false`、空错误、空媒体数组。命令不提供完整会话消息、用户反问或可区分的所有状态;不能仅凭这个响应断言具体进度、取消状态或等待用户输入。轮询停止条件见 [共用流程](../workflows/async-delivery.md)。
42
+
43
+ ## 下载行为
44
+
45
+ 文件名由 CLI 根据产物信息生成,以 `output_path` 为准,不自行拼接编号或推测扩展名。同目录已有同名文件可能被复用;复用不证明内容相同,也不代表本次新下载。发现同名文件属于其他产物时,选择用户认可范围内的未冲突目录再查询,不删除已有文件。
46
+
47
+ 找不到产物、链接缺失或下载失败时会返回错误。当前任一文件下载失败可能使整次查询只返回错误,无法据此认定其他文件都没下载或已完整交付。复查本次结果,不把输出目录里的任意旧文件当作本次产物。
@@ -0,0 +1,17 @@
1
+ # video-super-resolution:视频超分
2
+
3
+ 用于提升已有视频的分辨率或清晰度。将视频作为新内容参考时使用 [生视频](generate-video.md)。
4
+
5
+ | 参数 | 必填 | 规则 |
6
+ | --- | --- | --- |
7
+ | `--video` | 是 | 一个本地视频文件,命令内部上传 |
8
+ | `--output-resolution` | 是 | 用户选择的目标分辨率;帮助列出 `720p`、`1080p`、`2k`、`4k` |
9
+ | `--tool-version` | 否 | 用户指定时传入;帮助列出 `standard`、`professional_v1`、`professional_v2` |
10
+
11
+ 用户只说“变清晰”且未给目标分辨率时先询问。模型版本或分辨率最终合法性由服务端判断。
12
+
13
+ ```bash
14
+ pippit-tool-cli video-super-resolution --video "/path/to/source.mp4" --output-resolution 1080p
15
+ ```
16
+
17
+ 示例以用户要求 1080p 为前提。成功返回 `thread_id`、`run_id`、`web_thread_link`,按 [异步结果与媒体交付](../workflows/async-delivery.md) 查询、下载并交付。输入失败或服务端拒绝时停止,不擅自切换版本或分辨率。
@@ -0,0 +1,26 @@
1
+ # 场景:在已有画布创建角色节点
2
+
3
+ 用户请求:“在这个小云雀画布里新增一个叫小雨的角色节点。”用户已提供真实画布资产 ID,目标是新增资料节点,不是生成人物图片。
4
+
5
+ 先读 [Canvas 模块](../commands/canvas.md),运行 `ensure-cli.js --canvas` 并完成授权。将 `CANVAS_ENTRY` 替换为返回的 Node 入口路径,将 `CANVAS_ASSET_ID` 替换为用户画布资产 ID。
6
+
7
+ ```bash
8
+ node "CANVAS_ENTRY" canvas command list
9
+ node "CANVAS_ENTRY" canvas command describe create_biz_node --node-kind role
10
+ node "CANVAS_ENTRY" canvas command describe get_snapshot
11
+ node "CANVAS_ENTRY" canvas command run get_snapshot --canvas-id CANVAS_ASSET_ID --input '{}'
12
+ ```
13
+
14
+ 确认目标画布与既有节点,按当前 role schema 核对 `initialData.nodeName` 后执行:
15
+
16
+ ```bash
17
+ node "CANVAS_ENTRY" canvas command run create_biz_node --canvas-id CANVAS_ASSET_ID --input '{"nodeKind":"role","initialData":{"nodeName":"小雨"}}'
18
+ ```
19
+
20
+ 由业务工厂分配节点及配套资产,不自行编 ID。检查实际执行结果,再回读:
21
+
22
+ ```bash
23
+ node "CANVAS_ENTRY" canvas command run get_snapshot --canvas-id CANVAS_ASSET_ID --input '{}'
24
+ ```
25
+
26
+ 确认新增角色及名称,只报告“已新增角色节点”,附画布链接(已有时)和查询到的 ID。不调用独立生图命令,也不把新节点认定为已生成图片。失败或响应不确定时按 [画布编辑流程](../workflows/canvas-edit.md) 回读后恢复,避免创建重复节点。
@@ -0,0 +1,15 @@
1
+ # 场景:从首帧过渡到尾帧
2
+
3
+ 用户请求:“以 opening.png 为首帧、ending.png 为尾帧,生成从白天过渡到夜晚的视频。”
4
+
5
+ 完成 [入口](../SKILL.md) 的前置步骤,确认两张图片实际位于下面的路径,读取 [生视频命令](../commands/generate-video.md)。
6
+
7
+ ```bash
8
+ pippit-tool-cli generate-video \
9
+ --prompt "以 opening.png 为首帧、ending.png 为尾帧,生成从白天过渡到夜晚的视频。" \
10
+ --image "/path/to/opening.png" \
11
+ --image "/path/to/ending.png" \
12
+ --generate-type 1
13
+ ```
14
+
15
+ 第一个 `--image` 是首帧,第二个是尾帧,不按文件名重新排序。用户未指定模型、时长、比例和分辨率,省略这些参数。若用户仅给两张图片而未明确首尾角色,应先确认。随后执行 [异步结果与媒体交付](../workflows/async-delivery.md)。
@@ -0,0 +1,19 @@
1
+ # 基础示例:生成一张图并交付
2
+
3
+ 用户请求:“用 seedream_5.0_pro 生成一张白底红色马克杯图片。”
4
+
5
+ 先按 [入口](../SKILL.md) 完成安装检查与登录,读取 [生图命令](../commands/generate-image.md) 和 [异步交付流程](../workflows/async-delivery.md)。下列命令名替换为检查返回的 `cli_path`。
6
+
7
+ ```bash
8
+ pippit-tool-cli generate-image --prompt "用 seedream_5.0_pro 生成一张白底红色马克杯图片。" --model seedream_5.0_pro --generate-image-count 1
9
+ ```
10
+
11
+ 此处模型与数量均来自用户请求,不添加比例或分辨率。保存实际返回的任务 ID 并展示任务链接,然后将下面的占位符替换为真实值:
12
+
13
+ ```bash
14
+ pippit-tool-cli query-result --thread-id THREAD_ID --run-id RUN_ID --download-dir "./xyq_output"
15
+ ```
16
+
17
+ 按共用流程继续查询。成功后读取 `images[].output_path`,检查文件存在且非空,用宿主的附件工具或媒体渲染能力展示图片。回复可以是“已生成”加实际图片附件;只发送“文件位于 ./xyq_output/…”不算交付。
18
+
19
+ 如果用户只问“怎么生成一张马克杯图片”,解释用法即可,不执行此收费流程。基础视频生成同样复用交付流程,提交参数改读 [生视频命令](../commands/generate-video.md)。
@@ -0,0 +1,15 @@
1
+ # 场景:保留多张参考图的角色
2
+
3
+ 用户请求:“用 seedream_5.0_pro,图1是底图,图2只提供猫的形象,把图1的猫换成图2的猫,背景和其他物体不变。”
4
+
5
+ 用户明确图1为 `/path/to/scene.png`,图2为 `/path/to/cat.png`。完成 [入口](../SKILL.md) 的前置步骤后,读取 [生图命令](../commands/generate-image.md)。
6
+
7
+ ```bash
8
+ pippit-tool-cli generate-image \
9
+ --prompt "用 seedream_5.0_pro,图1是底图,图2只提供猫的形象,把图1的猫换成图2的猫,背景和其他物体不变。" \
10
+ --model seedream_5.0_pro \
11
+ --image "/path/to/scene.png" \
12
+ --image "/path/to/cat.png"
13
+ ```
14
+
15
+ 保留用户原文与素材顺序,不将两张图都理解成可自由混合的风格参考。图片角色或文件映射不明确时先确认。继续 [异步结果与媒体交付](../workflows/async-delivery.md),逐项展示实际结果;具备看图能力时检查用户要求保留的主要元素,不能未经查看就宣称完全满足编辑要求。
@@ -0,0 +1,21 @@
1
+ # 场景:擦字幕后超分
2
+
3
+ 用户请求:“把 /path/to/source.mp4 先擦掉字幕,再超分到 1080p。”
4
+
5
+ 用户已明确授权这两个步骤及顺序。完成 [入口](../SKILL.md) 的前置步骤,读取 [擦字幕](../commands/erase-video-subtitle.md)、[超分](../commands/video-super-resolution.md) 和 [异步交付流程](../workflows/async-delivery.md)。
6
+
7
+ 第一步:
8
+
9
+ ```bash
10
+ pippit-tool-cli erase-video-subtitle --video "/path/to/source.mp4"
11
+ pippit-tool-cli query-result --thread-id FIRST_THREAD_ID --run-id FIRST_RUN_ID --download-dir "./xyq_output"
12
+ ```
13
+
14
+ 查询使用第一步真实返回的 ID。按共用流程等待成功,核对下载的视频文件。将下面的 `FIRST_OUTPUT_PATH` 替换为对应 `videos[].output_path`,不能继续传最初的带字幕视频:
15
+
16
+ ```bash
17
+ pippit-tool-cli video-super-resolution --video "FIRST_OUTPUT_PATH" --output-resolution 1080p
18
+ pippit-tool-cli query-result --thread-id SECOND_THREAD_ID --run-id SECOND_RUN_ID --download-dir "./xyq_output"
19
+ ```
20
+
21
+ 使用第二步的新 ID 查询,并交付最终视频附件。第一步失败时不提交第二步;第二步失败时说明组合任务尚未完成,可交付明确标注“仅完成擦字幕”的中间视频。用户只要求擦字幕时,到第一步结束,不自行增加超分。
@@ -7,7 +7,7 @@ const os = require("os");
7
7
  const path = require("path");
8
8
 
9
9
  const REQUIRED_COMMANDS = [
10
- "login", "submit-run", "upload-file", "download-result", "query-result",
10
+ "status", "login", "logout", "query-result",
11
11
  "generate-image", "generate-video", "video-super-resolution",
12
12
  "erase-video-subtitle", "get-credit-balance",
13
13
  ];
@@ -44,7 +44,7 @@ function findCLIOnPath() {
44
44
  return null;
45
45
  }
46
46
 
47
- function ensureCLI() {
47
+ function ensureCLI({ canvas = false } = {}) {
48
48
  if (Number(process.versions.node.split(".")[0]) < 16) {
49
49
  throw new Error("需要 Node.js 16+ 和 npm。");
50
50
  }
@@ -79,17 +79,40 @@ function ensureCLI() {
79
79
  if (expectedVersion && version !== expectedVersion) {
80
80
  throw new Error(`CLI 版本 ${version} 与 npm 包版本 ${expectedVersion} 不一致。`);
81
81
  }
82
- for (const command of REQUIRED_COMMANDS) {
82
+ const commands = REQUIRED_COMMANDS.map((command) => [command]);
83
+ if (canvas) {
84
+ for (const command of ["create", "get", "allocate", "upload", "apply"]) commands.push(["canvas", command]);
85
+ }
86
+ for (const command of commands) {
83
87
  try {
84
- run(cliPath, [command, "--help"], `检查 ${command} 命令`, true);
88
+ run(cliPath, [...command, "--help"], `检查 ${command.join(" ")} 命令`, true);
85
89
  } catch (err) {
86
90
  if (Number.isInteger(err.exitStatus) && err.exitStatus !== 0) {
87
- err.missingCommand = command;
91
+ err.missingCommand = command.join(" ");
88
92
  }
89
93
  throw err;
90
94
  }
91
95
  }
92
- return { cli_path: cliPath, version };
96
+ const result = { cli_path: cliPath, version };
97
+ if (canvas) {
98
+ // Semantic Canvas commands live in the npm package, not the Go binary.
99
+ const entry = path.resolve(path.dirname(fs.realpathSync(cliPath)), "../scripts/run.js");
100
+ try {
101
+ if (!fs.existsSync(entry)) throw new Error("缺少 npm 入口");
102
+ const catalog = JSON.parse(run(process.execPath, [entry, "canvas", "command", "list"], "检查 Canvas 运行时", true));
103
+ if (!Array.isArray(catalog.commands) || !["get_snapshot", "create_biz_node"].every(
104
+ (name) => catalog.commands.some((command) => command.name === name),
105
+ )) throw new Error("Canvas 命令目录不完整");
106
+ } catch (err) {
107
+ // Runtime timeouts are execution failures; do not repeatedly install to mask them.
108
+ if (err.exitStatus === null) throw err;
109
+ const failure = new Error("Canvas npm 入口或运行时不可用,请检查 CLI 安装。");
110
+ failure.missingCommand = "canvas command runtime";
111
+ throw failure;
112
+ }
113
+ result.canvas_entry = entry;
114
+ }
115
+ return result;
93
116
  }
94
117
 
95
118
  const candidates = new Set([findCLIOnPath(), fs.existsSync(cachedCLI) ? cachedCLI : null]);
@@ -123,7 +146,10 @@ function ensureCLI() {
123
146
  // Preserve the previous installation until the replacement passes all checks.
124
147
  fs.rmSync(installedDir, { recursive: true, force: true });
125
148
  fs.renameSync(installDir, installedDir);
126
- return { ...result, cli_path: cachedCLI };
149
+ return {
150
+ ...result, cli_path: cachedCLI,
151
+ ...(canvas ? { canvas_entry: fs.realpathSync(path.join(installedDir, "node_modules", "@pippit-dev", "cli", "scripts", "run.js")) } : {}),
152
+ };
127
153
  } catch (err) {
128
154
  fs.rmSync(installDir, { recursive: true, force: true });
129
155
  throw err;
@@ -132,13 +158,13 @@ function ensureCLI() {
132
158
 
133
159
  if (require.main === module) {
134
160
  if (process.argv.length === 3 && process.argv[2] === "--help") {
135
- console.log("Usage: node ensure-cli.js\n优先复用 PATH 或缓存中命令齐全的 CLI,不存在或缺少必需命令时安装 npm latest,成功输出 {cli_path, version} JSON。");
136
- } else if (process.argv.length !== 2) {
137
- console.error("不支持的参数。用法:node ensure-cli.js");
161
+ console.log("Usage: node ensure-cli.js [--canvas]\n优先复用 PATH 或缓存中命令齐全的 CLI,不存在或缺少必需命令时安装 npm latest,成功输出 {cli_path, version} JSON。--canvas 额外验证画布原生命令和 npm 运行时,并返回 canvas_entry。");
162
+ } else if (process.argv.length !== 2 && !(process.argv.length === 3 && process.argv[2] === "--canvas")) {
163
+ console.error("不支持的参数。用法:node ensure-cli.js [--canvas]");
138
164
  process.exitCode = 1;
139
165
  } else {
140
166
  try {
141
- console.log(JSON.stringify(ensureCLI()));
167
+ console.log(JSON.stringify(ensureCLI({ canvas: process.argv[2] === "--canvas" })));
142
168
  } catch (err) {
143
169
  console.error(err.message);
144
170
  process.exitCode = 1;
@@ -0,0 +1,39 @@
1
+ # 检查与按需安装 CLI
2
+
3
+ 普通任务开始时运行与本文同目录的脚本:
4
+
5
+ ```bash
6
+ node "{baseDir}/scripts/ensure-cli.js"
7
+ ```
8
+
9
+ `{baseDir}` 是当前 Skill 的根目录。运行环境需 Node.js 16+,并允许执行本地程序。首次安装或自动升级还需要 npm、可写的用户缓存目录、访问 npm 源和 GitHub Release 的网络、`curl` 与解压工具(macOS/Linux 的 `tar`,Windows 的 PowerShell)。复用已有 CLI 不需要下载网络或 npm。
10
+
11
+ ## 查找与复用
12
+
13
+ 脚本依次检查 PATH 中的 CLI 和自身缓存,验证版本及本 Skill 使用命令的 `--help`;命令集合维护在脚本的 `REQUIRED_COMMANDS`。帮助检查不调用生成服务,也不需要凭据,不证明账号权限或服务端运行状态。
14
+
15
+ 命令齐全则直接复用,不检查最新版本;不存在或缺少必需命令时,获取 `@pippit-dev/cli@latest`。PATH 旧版本缺少命令但缓存完整时复用缓存,避免每次升级。版本命令不能运行或检查超时则报告运行错误。
16
+
17
+ 需要安装时,脚本跳过 npm 生命周期脚本获取包,调用包内 `scripts/install-cli.js` 只安装 CLI。成功后缓存到 `~/.cache/pippit-tool-cli/xyq-skill/<平台>-<架构>/current`。不要求全局 npm 写入权限,也不安装或清理全局 Skill。
18
+
19
+ ## 返回与使用
20
+
21
+ 日志写入 stderr,成功时 stdout 为 JSON:
22
+
23
+ ```json
24
+ {"cli_path":"/absolute/path/to/pippit-tool-cli","version":"实际版本"}
25
+ ```
26
+
27
+ 后续所有命令使用返回的 `cli_path`,路径加引号;Windows PowerShell 使用 `& "绝对路径" 参数`。不要依赖前一次 shell 中的临时变量,同一任务复用返回路径,路径被清理后再运行脚本。
28
+
29
+ 每次最多安装一次,升级成功前保留旧缓存。下载失败、最新包缺少安装入口或仍缺必需命令时停止并报告,不重复升级、不输出可用路径。仅将升级到可用版本作为恢复方式,不绕过缺失命令的检查。
30
+
31
+ 独立 ZIP 必须包含整个 Skill 的命令文档、共用流程、示例和本脚本,保持相对路径;安装入口来自下载的 npm 包,不依赖本机源码仓库。
32
+
33
+ ## Canvas 运行时检查
34
+
35
+ 画布任务改用 `node "{baseDir}/scripts/ensure-cli.js" --canvas`。除原有检查外,再验证五个原生资产子命令的帮助,并通过同一个 npm 包的 Node 入口真实执行离线 `canvas command list`,确认运行时可加载且含基本查询与节点创建能力。该检查不登录、不访问画布、不写入远端。
36
+
37
+ 成功额外返回 `canvas_entry`,供 `node "CANVAS_ENTRY" canvas command ...` 使用;`cli_path` 仍用于原生命令。语义操作的实际支持范围以当前目录为准,检查通过不代表所有业务操作或服务端权限都可用。
38
+
39
+ 独立 Go 二进制没有 npm 入口,或包内运行时缺失/损坏时,Canvas 模式按原有规则检查缓存并至多安装一次最新完整 npm 包,保留旧安装直到新版本通过。普通媒体任务不要求 Canvas 运行时,也不会因为缺少它而升级。不要把缓存内的 `run.js` 单独复制出来,它依赖相邻模块与 `dist` 运行时。
@@ -0,0 +1,29 @@
1
+ # 异步结果与媒体交付
2
+
3
+ 生成、视频处理、查询已有结果共用此流程。开始前读取 [查询命令契约](../commands/query-result.md),不得只按退出码或 `completed` 判断成功。
4
+
5
+ ## 保存任务与查询
6
+
7
+ 1. 提交成功后保存 `thread_id`、`run_id` 和 `web_thread_link`,立即向用户展示任务链接。已有任务直接使用其标识进入查询,不再次生成。
8
+ 2. 使用同一 `cli_path` 和同一对任务 ID 调用查询命令,明确 `--download-dir`。每隔 10 秒查询一次,不在轮询中重新安装 CLI。
9
+ 3. 先检查命令是否可执行、输出是否为有效 JSON,再读取非空 `error_message`;有错误就进入下面的失败处理。
10
+ 4. 无错误且 `completed=false` 时继续轮询,只说明尚未取得最终结果,不编造创作消息或完成百分比。
11
+ 5. 无错误且 `completed=true` 时,要求媒体数组中有待交付产物,逐项核对 `output_path` 后进入交付。空结果或缺少必要字段时报告异常,不空轮询。
12
+
13
+ ## 停止与恢复
14
+
15
+ - 明确的任务失败、鉴权失败、参数错误或产物缺失:停止并报告错误及可用的任务 ID、链接、LogID,不重新提交生成。
16
+ - 可识别的暂时网络错误或下载错误:间隔 10 秒后仅重试同一次查询一次;仍失败则报告阻塞。无法判别错误类别时停止,避免盲目重试。
17
+ - 用户要求停止时停止本地轮询;这不等于已取消服务端任务。持续未完成时遵守用户或宿主的等待时限,最迟在连续 48 小时后停止等待,保留 ID 供后续恢复查询。宿主无法持续执行时如实说明,不承诺后台监控。
18
+ - 当前查询输出无法区分所有非成功状态;若长时间未完成或任务页面显示需要交互,报告当前查询能力的限制,不代答或新建任务。
19
+ - 恢复时继续查询原任务,复用该任务已确定的输出目录,对已交付产物去重。
20
+
21
+ ## 媒体交付完成标准
22
+
23
+ - 对 `images[]`、`videos[]` 中每个待交付文件确认本地存在且非空;同名旧文件只有明确对应本次产物时才能复用,不能计为本次新下载。
24
+ - 使用宿主实际提供的文件交付工具,逐项展示真实图片/视频附件。宿主支持内置媒体渲染时按其规定引用文件,例如要求绝对路径时,先解析 `output_path` 为绝对路径。
25
+ - 产物 URL、任务链接或本地文件列表只能作为补充,不能替代媒体附件或可预览媒体。
26
+ - 所有待交付产物均经宿主交付成功后,才能宣称“交付完成”。生成、下载、附件交付分别判断。
27
+ - 某项下载或展示失败时明确未交付项目及原因,仍交付其他可确认属于本任务的可用媒体。宿主不支持媒体展示时如实说明限制,不宣称全部交付完成。
28
+
29
+ 组合处理时,中间产物下载并检查成功后才可作为下一步输入;最终结果按上述标准交付。用户要求中间产物时也逐项交付。
@@ -0,0 +1,19 @@
1
+ # 画布查询、编辑与验证
2
+
3
+ 适用于 [Canvas](../commands/canvas.md) 任务。该流程以画布状态为交付对象,创建、编辑不会自动产生可交付媒体文件。
4
+
5
+ 1. **准备入口**:执行 Canvas 安装检查,保存 `cli_path` 和 `canvas_entry`,按授权文档确认身份。只查命令目录和离线指南无需登录;查询真实画布、执行编辑需要授权。
6
+ 2. **确定目标**:已有画布使用真实 `canvas_asset_id` 作为语义命令的 `--canvas-id`。用户提供的信息无法唯一定位时先补齐,不创建替代画布。新画布仅在用户要求创建时使用原生 `canvas create`,检查其返回状态。
7
+ 3. **发现与查询**:`list → describe/schema` 定位操作。用 `get_snapshot` 查节点,用 `get_asset` 查具体资产;3D/多轨再查询其独立文档。`run` 是统一执行入口,其中 query/get 类操作是读取,不等于所有 run 都是写入。
8
+ 4. **准备输入**:使用查询所得的节点、子对象和资产 ID。多轨 `expectedRevision` 来自最新查询。复杂输入写入 JSON 文件后传 `--file`;不要同时传 `--file` 和 `--input`。只操作用户指定范围。
9
+ 5. **预演与写入**:当前 schema 支持 `dryRun` 时可先预演,再在已有授权范围内执行真实修改。不能给所有命令强加此字段:领域命令的 dryRun 验证具体编辑,通用 `apply_mutations.dryRun` 不能替代领域预演。删除、恢复等操作仅按用户明确要求执行。
10
+ 6. **验证结果**:检查退出码与 JSON 的 `ok`、错误信息,`ok=false` 即失败。`dryRun=true` 成功不代表落盘;实际写入成功后,用对应查询回读目标字段及关联关系,确认没有误改其他对象。
11
+ 7. **反馈交付**:说明已验证的画布变化,保留已有/返回的画布链接及目标 ID。只完成文档编辑时不要声称图片/视频已生成;确有媒体文件待交付时遵循 [媒体交付标准](async-delivery.md)。
12
+
13
+ 语义执行骨架(占位符替换为当前目录中的命令、真实 ID 和已准备的输入文件):
14
+
15
+ ```bash
16
+ node "CANVAS_ENTRY" canvas command run COMMAND_NAME --canvas-id CANVAS_ASSET_ID --file "/path/to/input.json"
17
+ ```
18
+
19
+ 写请求失败或返回“未确认事务已隔离”时保留错误上下文,先回读状态,不自动重跑、不删除本地恢复记录。多轨版本冲突需重新查询并基于新状态准备操作;检查点恢复会改变画布,不能作为所有失败的自动回滚方式。
@@ -1,53 +0,0 @@
1
- #!/usr/bin/env python3
2
- """查询会话进展:POST /api/biz/v1/skill/get_thread,返回消息列表"""
3
-
4
- import argparse
5
- import json
6
- import sys
7
- import os
8
-
9
- sys.path.insert(0, os.path.dirname(__file__))
10
- from xyq_common import extract_entries_from_run
11
- from xyq_common import get_thread
12
-
13
-
14
- def main():
15
- parser = argparse.ArgumentParser(
16
- description="查询会话消息列表(会话进展)",
17
- epilog="""
18
- 环境变量:
19
- XYQ_ACCESS_KEY 必填,Bearer 鉴权
20
- API 地址固定为 https://xyq.jianying.com,不支持环境变量覆盖
21
-
22
- 示例:
23
- python3 get_thread.py --thread-id abc123 --run-id def456 --after-seq 0
24
- """,
25
- formatter_class=argparse.RawDescriptionHelpFormatter,
26
- )
27
- parser.add_argument(
28
- "--thread-id",
29
- required=True,
30
- help="会话 ID(由 submit_run 返回)",
31
- )
32
- parser.add_argument(
33
- "--run-id",
34
- default="",
35
- help="运行 ID(由 submit_run 返回)",
36
- )
37
- parser.add_argument(
38
- "--after-seq",
39
- type=int,
40
- default=0,
41
- help="只返回 seq 大于等于该值的消息,用于增量拉取(默认 0)",
42
- )
43
- args = parser.parse_args()
44
-
45
- run = get_thread(args.thread_id, run_id=args.run_id, after_seq=args.after_seq)
46
- # 从run中提取Message和Artifact
47
- entries = extract_entries_from_run(run)
48
- out = {"messages": entries}
49
- print(json.dumps(out, ensure_ascii=False, indent=2))
50
-
51
-
52
- if __name__ == "__main__":
53
- main()