@pippit-dev/cli 1.0.24 → 1.0.26
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.
- package/README.md +29 -189
- package/checksums.txt +6 -6
- package/cmd/generate_video/generate_video.go +3 -3
- package/cmd/generate_video_test.go +57 -55
- package/dist/checksums.txt +6 -6
- package/internal/generate_video/generate_video.go +0 -15
- package/package.json +1 -1
- package/skills/xyq-nest-skill/SKILL.md +40 -495
- package/skills/xyq-nest-skill/commands/auth.md +35 -0
- package/skills/xyq-nest-skill/commands/canvas-assets.md +32 -0
- package/skills/xyq-nest-skill/commands/canvas.md +50 -0
- package/skills/xyq-nest-skill/commands/erase-video-subtitle.md +13 -0
- package/skills/xyq-nest-skill/commands/generate-image.md +32 -0
- package/skills/xyq-nest-skill/commands/generate-video.md +33 -0
- package/skills/xyq-nest-skill/commands/get-credit-balance.md +17 -0
- package/skills/xyq-nest-skill/commands/query-result.md +47 -0
- package/skills/xyq-nest-skill/commands/video-super-resolution.md +17 -0
- package/skills/xyq-nest-skill/examples/canvas-role.md +26 -0
- package/skills/xyq-nest-skill/examples/first-last-frame.md +15 -0
- package/skills/xyq-nest-skill/examples/generate-and-deliver.md +19 -0
- package/skills/xyq-nest-skill/examples/image-edit.md +15 -0
- package/skills/xyq-nest-skill/examples/video-process-chain.md +21 -0
- package/skills/xyq-nest-skill/scripts/ensure-cli.js +37 -11
- package/skills/xyq-nest-skill/scripts/install.md +39 -0
- package/skills/xyq-nest-skill/workflows/async-delivery.md +29 -0
- package/skills/xyq-nest-skill/workflows/canvas-edit.md +19 -0
- package/skills/xyq-nest-skill/scripts/get_thread.py +0 -53
- package/skills/xyq-nest-skill/scripts/xyq_common.py +0 -162
package/README.md
CHANGED
|
@@ -9,210 +9,50 @@
|
|
|
9
9
|
| 技能 | 说明 | 路径 |
|
|
10
10
|
|-------|-------------|------|
|
|
11
11
|
| `xyq-short-drama-skill` | 短剧工作流技能,支持提交创作任务、上传参考文件、查询进度、列出会话文件和下载产物。 | `skills/short-drama/` |
|
|
12
|
-
| `xyq-skill` |
|
|
12
|
+
| `xyq-skill` | 图片生成与参考图编辑、视频生成、视频超分与擦字幕、异步结果交付、个人 Canvas 编辑、积分查询及登录授权。 | `skills/xyq-nest-skill/` |
|
|
13
13
|
|
|
14
14
|
### 技能路由
|
|
15
15
|
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
- 用户明确要求视频模型直出、指定视频模型或直接调用 CLI 时,由 `xyq-skill` 调用 `pippit-tool-cli generate-video`,再用 `query-result` 查询和下载结果。
|
|
19
|
-
- 短剧生成、续写、改写、人物设定、分集创作和短剧会话文件处理使用 `xyq-short-drama-skill`,不要与通用创作流程混用。
|
|
16
|
+
- 图片生成与参考图编辑、视频生成(含首尾帧和参考素材)、视频超分、擦字幕、结果查询、个人 Canvas 编辑、积分和授权由 `xyq-skill` 处理。
|
|
17
|
+
- 短剧生成、续写、改写、人物设定、分集创作和短剧会话文件处理使用 `xyq-short-drama-skill`。
|
|
20
18
|
|
|
21
|
-
|
|
19
|
+
需要补充、选择或确认时,使用宿主实际暴露且当前模式允许的工具:Codex 的 `request_user_input` / `request_user_input_async`、WorkBuddy 的 `ask_user_question`;不可用时用普通聊天。
|
|
22
20
|
|
|
23
|
-
##
|
|
21
|
+
## 小云雀图片、视频与媒体处理技能
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
入口:[skills/xyq-nest-skill/SKILL.md](skills/xyq-nest-skill/SKILL.md)。普通生成请求也直接使用对应 CLI;素材路径交给命令内部上传,异步查询自动下载,最后通过宿主交付真实媒体附件。
|
|
26
24
|
|
|
27
|
-
|
|
25
|
+
| 操作 | CLI | 文档 |
|
|
26
|
+
| --- | --- | --- |
|
|
27
|
+
| 登录授权 | `status` / `login` / `logout` | [授权](skills/xyq-nest-skill/commands/auth.md) |
|
|
28
|
+
| 个人 Canvas 画布与节点编辑 | `canvas` | [画布](skills/xyq-nest-skill/commands/canvas.md) |
|
|
29
|
+
| 生图、参考图编辑 | `generate-image` | [图片](skills/xyq-nest-skill/commands/generate-image.md) |
|
|
30
|
+
| 生视频、首尾帧 | `generate-video` | [视频](skills/xyq-nest-skill/commands/generate-video.md) |
|
|
31
|
+
| 视频超分 | `video-super-resolution` | [超分](skills/xyq-nest-skill/commands/video-super-resolution.md) |
|
|
32
|
+
| 擦字幕 | `erase-video-subtitle` | [擦字幕](skills/xyq-nest-skill/commands/erase-video-subtitle.md) |
|
|
33
|
+
| 查询并下载结果 | `query-result` | [查询](skills/xyq-nest-skill/commands/query-result.md) |
|
|
34
|
+
| 查积分 | `get-credit-balance` | [积分](skills/xyq-nest-skill/commands/get-credit-balance.md) |
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
|------|------|
|
|
31
|
-
| 创建会话 / 发送消息 | 向小云雀发送自然语言指令,生成图片或视频。 |
|
|
32
|
-
| 查询会话进展 | 增量拉取会话消息,轮询创作进度和产物结果。 |
|
|
33
|
-
| 积分余额查询 | 调用 `get-credit-balance`,读取 `total_remain_amount` 并展示个人有效积分余额。 |
|
|
34
|
-
| 上传文件 | 上传图片/视频/mp3或wav音频到小云雀资产库,获取 `asset_id` 用于编辑和参考。 |
|
|
35
|
-
| 下载结果 | 批量下载生成的图片/视频到本地,支持并行下载。 |
|
|
36
|
-
| 视频模型直出 | 调用 `generate-video` 提交请求,展示 `web_thread_link`,再用 `query-result` 查询并下载视频。 |
|
|
37
|
-
|
|
38
|
-
小云雀平台能力覆盖:
|
|
39
|
-
|
|
40
|
-
- 生成:文生图、文生视频、图生视频、视频续写。
|
|
41
|
-
- 编辑:局部修改、元素替换、镜头调整、风格迁移。
|
|
42
|
-
- 复杂创作:复刻视频/TVC/宣传片、音乐 MV 生成、产品展示片制作。
|
|
43
|
-
|
|
44
|
-
### 配置
|
|
45
|
-
|
|
46
|
-
单独上传 `xyq-skill` ZIP 时,保留 Skill 内的 `scripts/ensure-cli.js`。每次开始执行 Skill 任务先运行:
|
|
36
|
+
### 安装与执行
|
|
47
37
|
|
|
48
38
|
```bash
|
|
49
39
|
node /path/to/xyq-skill/scripts/ensure-cli.js
|
|
50
40
|
```
|
|
51
41
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
新入口 `node scripts/install-cli.js` 只安装 npm 包对应版本的 CLI 二进制,不安装或清理全局 Skill。ZIP 安装脚本先以 `--ignore-scripts` 获取 npm 包,再调用这个入口。发布包含新入口的 npm 包及对应 GitHub Release 后,ZIP 的最新版本安装流程才能完整使用。
|
|
55
|
-
|
|
56
|
-
`submit-run` 和 `upload-file` 使用原生 CLI 登录凭证(`pippit-tool-cli login`),也可通过 `XYQ_ACCESS_KEY` 显式覆盖。保留的查询脚本 `get_thread.py` 仍需配置同一用户的 Bearer 凭证:
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
export XYQ_ACCESS_KEY="<access-key>"
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
CLI 和 Python 脚本携带用户密钥的 API 请求地址固定为 `https://xyq.jianying.com`,不接受 `XYQ_OPENAPI_BASE` / `XYQ_BASE_URL` 覆盖。CLI 拒绝 API 跨域重定向,Python API 脚本禁止自动重定向。上传只在 Authorization 请求头携带密钥。
|
|
63
|
-
|
|
64
|
-
### 创建会话 / 发送消息
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
# 创建新会话
|
|
68
|
-
pippit-tool-cli submit-run --message "生一个动漫视频"
|
|
69
|
-
|
|
70
|
-
# 向已有会话发送消息
|
|
71
|
-
pippit-tool-cli submit-run \
|
|
72
|
-
--message "再生成一个故事视频" \
|
|
73
|
-
--thread-id THREAD_ID
|
|
74
|
-
|
|
75
|
-
# 携带参考文件发送
|
|
76
|
-
pippit-tool-cli submit-run \
|
|
77
|
-
--message "参考这个视频做修改" \
|
|
78
|
-
--asset-ids asset_id1 --asset-ids asset_id2
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
| 参数 | 必填 | 说明 |
|
|
82
|
-
|------|------|------|
|
|
83
|
-
| `--message` | 是 | 非空白的创作指令,原样发送。 |
|
|
84
|
-
| `--thread-id` | 否 | 已有会话 ID,不传则创建新会话。 |
|
|
85
|
-
| `--asset-ids` | 否 | 每次传一个资产 ID;多个素材重复该参数。 |
|
|
86
|
-
|
|
87
|
-
返回示例:
|
|
88
|
-
|
|
89
|
-
```json
|
|
90
|
-
{
|
|
91
|
-
"thread_id": "90f05e0c-...",
|
|
92
|
-
"run_id": "abc123-...",
|
|
93
|
-
"web_thread_link": "https://xyq.jianying.com/..."
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### 查询会话进展
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
python3 skills/xyq-nest-skill/scripts/get_thread.py \
|
|
101
|
-
--thread-id THREAD_ID \
|
|
102
|
-
--run-id RUN_ID \
|
|
103
|
-
--after-seq 0
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
| 参数 | 必填 | 说明 |
|
|
107
|
-
|------|------|------|
|
|
108
|
-
| `--thread-id` | 是 | 会话 ID。 |
|
|
109
|
-
| `--run-id` | 否 | 运行 ID。 |
|
|
110
|
-
| `--after-seq` | 否 | 增量拉取起始序号,默认 `0`。 |
|
|
111
|
-
|
|
112
|
-
脚本会返回会话消息和产物条目。后续轮询时,根据已获取消息更新 `after_seq`。
|
|
113
|
-
|
|
114
|
-
### 上传文件
|
|
115
|
-
|
|
116
|
-
使用顶层 `upload-file --path`,每次上传一个本地图片、视频或 MP3/WAV 音频文件,文件须小于 500 MB(500000000 字节)。成功返回 `{"asset_id":"..."}`,可直接传给 `submit-run --asset-ids`。
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
# 上传图片
|
|
120
|
-
pippit-tool-cli upload-file --path /path/to/image.png
|
|
121
|
-
|
|
122
|
-
# 上传视频
|
|
123
|
-
pippit-tool-cli upload-file --path /path/to/video.mp4
|
|
124
|
-
|
|
125
|
-
# 上传音频
|
|
126
|
-
pippit-tool-cli upload-file --path /path/to/audio.mp3
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
仅支持 `image/*`、`video/*` 和 `.mp3/.wav` 音频文件,单文件大小限制 500 MB。
|
|
130
|
-
|
|
131
|
-
返回示例:
|
|
132
|
-
|
|
133
|
-
```json
|
|
134
|
-
{
|
|
135
|
-
"asset_id": "asset_xxx"
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### 下载结果
|
|
140
|
-
|
|
141
|
-
每个产物 URL 调用一次 CLI,`--output-path` 必须包含文件名:
|
|
42
|
+
保存返回的 `cli_path`,后续用带引号的绝对路径替换示例中的命令名。同一任务复用路径;已有命令齐全的 CLI 不下载,缺少必需命令时自动升级。ZIP 应包含整个 Skill 目录,具体环境条件与故障处理见 [安装说明](skills/xyq-nest-skill/scripts/install.md)。`node scripts/install-cli.js` 是 npm 包内仅安装 CLI 的入口,不安装或清理全局 Skill。
|
|
142
43
|
|
|
143
|
-
|
|
144
|
-
pippit-tool-cli download-result \
|
|
145
|
-
--url "URL1" \
|
|
146
|
-
--output-path "./xyq_output/storyboard_01.png"
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
| 参数 | 必填 | 说明 |
|
|
150
|
-
|------|------|------|
|
|
151
|
-
| `--url` | 是 | 单个产物的下载 URL。 |
|
|
152
|
-
| `--output-path` | 是 | 包含文件名的本地目标路径。 |
|
|
153
|
-
| `--updated-at` | 否 | 远端文件真实更新时间(Unix 秒),用于判断是否覆盖已有文件。 |
|
|
154
|
-
| `--workers` | 否 | 下载 worker 数,默认 `5`;当前单 URL 调用实际只使用一个 worker。 |
|
|
155
|
-
|
|
156
|
-
Skill 沿用用户指定的输出目录,未指定时使用 `./xyq_output`,按 URL 列表顺序从 `01` 编号,组成 `前缀_01.ext`(无前缀时为 `01.ext`)。扩展名优先取 URL 查询参数 `filename`,其次取 URL 路径,无法取得时使用 `.bin`。多文件逐项调用,可最多并行执行 5 个命令;重试保持原目标路径。
|
|
157
|
-
|
|
158
|
-
下载成功返回示例:
|
|
159
|
-
|
|
160
|
-
```json
|
|
161
|
-
{
|
|
162
|
-
"output_path": "./xyq_output/storyboard_01.png",
|
|
163
|
-
"downloaded": ["./xyq_output/storyboard_01.png"]
|
|
164
|
-
}
|
|
165
|
-
```
|
|
44
|
+
Canvas 任务使用 `ensure-cli.js --canvas`,额外返回 `canvas_entry`;原生资产命令使用 `cli_path`,语义命令通过 `node "CANVAS_ENTRY" canvas command ...` 执行。检查会真实加载 npm 内的离线命令目录,避免把原生帮助误当作运行时已就绪。画布编辑使用独立的 [查询、编辑与回读流程](skills/xyq-nest-skill/workflows/canvas-edit.md),不套用媒体轮询。
|
|
166
45
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
下载失败时命令以非零退出码返回错误,不保证输出 JSON。Skill 汇总各次调用的下载成功、已存在跳过和失败项,只对失败项重试一次;仍有失败时明确报告未完整交付。
|
|
170
|
-
|
|
171
|
-
### 典型示例
|
|
172
|
-
|
|
173
|
-
文生视频:
|
|
174
|
-
|
|
175
|
-
```text
|
|
176
|
-
1. pippit-tool-cli submit-run --message "生成一个赛博朋克风格的城市夜景视频"
|
|
177
|
-
2. 每 10 秒轮询:
|
|
178
|
-
get_thread.py --thread-id THREAD_ID --run-id RUN_ID --after-seq SEQUENCE
|
|
179
|
-
3. 拿到产物 URL 后下载:
|
|
180
|
-
pippit-tool-cli download-result --url "URL1" --output-path "./output/cyberpunk_01.mp4"
|
|
181
|
-
pippit-tool-cli download-result --url "URL2" --output-path "./output/cyberpunk_02.mp4"
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
编辑已有视频:
|
|
185
|
-
|
|
186
|
-
```text
|
|
187
|
-
1. pippit-tool-cli upload-file --path /path/to/video.mp4
|
|
188
|
-
2. pippit-tool-cli submit-run --message "把背景换成星空" --asset-ids asset_id
|
|
189
|
-
3. 按文生视频流程轮询和下载。
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
多参考图/视频生成:
|
|
193
|
-
|
|
194
|
-
```text
|
|
195
|
-
1. pippit-tool-cli upload-file --path /path/to/ref1.png
|
|
196
|
-
2. pippit-tool-cli upload-file --path /path/to/ref2.png
|
|
197
|
-
3. pippit-tool-cli upload-file --path /path/to/ref3.mp4
|
|
198
|
-
4. pippit-tool-cli submit-run --message "根据参考图和视频生成科普故事视频" --asset-ids asset_id1 --asset-ids asset_id2 --asset-ids asset_id3
|
|
199
|
-
5. 按文生视频流程轮询和下载。
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
在已有会话中追加需求:
|
|
203
|
-
|
|
204
|
-
```text
|
|
205
|
-
1. pippit-tool-cli submit-run --message "把刚才的视频加个片头" --thread-id EXISTING_THREAD_ID
|
|
206
|
-
2. 使用新的 run_id 轮询和下载。
|
|
207
|
-
```
|
|
46
|
+
登录后选择生成或处理命令,统一接入 [异步结果与媒体交付](skills/xyq-nest-skill/workflows/async-delivery.md)。完整基础案例见 [生成一张图并交付](skills/xyq-nest-skill/examples/generate-and-deliver.md),组合案例由入口按需引导。
|
|
208
47
|
|
|
209
|
-
|
|
48
|
+
### 模块维护
|
|
210
49
|
|
|
211
|
-
-
|
|
212
|
-
-
|
|
213
|
-
-
|
|
214
|
-
-
|
|
215
|
-
-
|
|
50
|
+
- `SKILL.md` 维护能力边界、意图到命令的路由及必要执行规则。
|
|
51
|
+
- `commands/` 每个模块维护适用场景、必填与可选参数、最小调用、真实返回契约及失败处理;授权相关命令合并在同一文档。
|
|
52
|
+
- `workflows/` 维护共用轮询与媒体交付规则;`examples/` 展示基础完整流程及易混淆的组合场景,引用规则,不复制参数手册。
|
|
53
|
+
- 新增 CLI 时补命令文档、入口路由、`ensure-cli.js` 必需命令集合和安装测试;声明是同步结果还是异步任务,是否需要附加运行时及其检查方式,按需接入交付流程,补正常、缺输入和易混淆场景用例。
|
|
54
|
+
- 文档使用 Skill 内相对链接,打包时保留结构。规范副本位于 `skills/xyq-nest-skill/`,项目发现入口 `.agents/skills/xyq-skill` 指向该目录。
|
|
55
|
+
- 修改后运行 `node scripts/skills.test.js` 与 `node scripts/install-cli.test.js`,检查引用完整、保留命令与安装检查一致及缺命令升级/缓存复用;Agent 行为用例见 [测试场景](skills/xyq-nest-skill/tests/agent_test_cases.md)。这些检查不代表真实生成已验证。
|
|
216
56
|
|
|
217
57
|
## 短剧工作流技能
|
|
218
58
|
|
|
@@ -387,7 +227,7 @@ pippit-tool-cli generate-video \
|
|
|
387
227
|
--resolution "720p"
|
|
388
228
|
```
|
|
389
229
|
|
|
390
|
-
命令输出 `thread_id`、`run_id` 和 `web_thread_link`。提交生视频 HTTP 请求时,参考图、参考视频和参考音频会使用上传接口返回的 `pippit_asset_id`,并分别写入 `video_part_tool_param.images`、`video_part_tool_param.videos` 和 `video_part_tool_param.audios
|
|
230
|
+
命令输出 `thread_id`、`run_id` 和 `web_thread_link`。提交生视频 HTTP 请求时,参考图、参考视频和参考音频会使用上传接口返回的 `pippit_asset_id`,并分别写入 `video_part_tool_param.images`、`video_part_tool_param.videos` 和 `video_part_tool_param.audios`。图片支持 `.jpg`、`.jpeg`、`.png`、`.gif`、`.bmp`、`.webp`、`.svg`;视频支持 `.mp4`、`.avi`、`.mov`、`.wmv`、`.flv`、`.webm`、`.mkv`、`.m4v`;音频仅支持 `.mp3`、`.wav`。普通用户支持模型 `Seedance_2.0_mini_lite`;`seedance2.0_vision`、`seedance2.0_fast_vision`、`Seedance_2.0_mini` 和 `Seedance_2.5` 为 VIP 专属模型。CLI 会在提交前校验 prompt 和文件后缀;模型、比例、分辨率等语义校验由服务端处理。
|
|
391
231
|
|
|
392
232
|
首尾帧生视频时,按首帧、尾帧的顺序传入两次 `--image`,并设置 `--generate-type 1`:
|
|
393
233
|
|
|
@@ -451,4 +291,4 @@ pippit-tool-cli query-result \
|
|
|
451
291
|
|
|
452
292
|
原生 CLI 命令通过 `pippit-tool-cli login` 打开小云雀网页授权,并把本机设备专属凭证保存到系统安全凭证库;Access Key 不会显示在终端。可用 `pippit-tool-cli status` 查看状态、`pippit-tool-cli logout` 清除本机登录。
|
|
453
293
|
|
|
454
|
-
CI 或 Agent 可继续显式设置 `XYQ_ACCESS_KEY
|
|
294
|
+
CI 或 Agent 可继续显式设置 `XYQ_ACCESS_KEY`,它会覆盖本机网页登录凭证;配置错误时不会静默回退到个人登录。会话提交和查询共享上述凭据。
|
package/checksums.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
485d9e51092beaf675a872f890fd744460e7afe45b27ec500ab6b5c90ebb6d0c pippit-tool-cli-1.0.26-darwin-amd64.tar.gz
|
|
2
|
+
978cca38261d539ea8ce2bcfd15deb24b4b317f38c084fdde92a5b9ca51f0739 pippit-tool-cli-1.0.26-darwin-arm64.tar.gz
|
|
3
|
+
d772c3fcef2c7b7ff0e6e52d6f10b2ee4cff1cd5fbdff9f08e0bd252ac378e79 pippit-tool-cli-1.0.26-linux-amd64.tar.gz
|
|
4
|
+
b16e7f2e7d9bbfe122eb0895ae6769a00d230ca250717aebb3f9b8eea0c04eae pippit-tool-cli-1.0.26-linux-arm64.tar.gz
|
|
5
|
+
fd05b98a6001602efad98731a4777cb035eea2bea2acbccb2c1d02af0223176e pippit-tool-cli-1.0.26-windows-amd64.zip
|
|
6
|
+
a0c873456cf254b0e452c6e376efec5a425ef044b3a1a671d063fa5a613242d0 pippit-tool-cli-1.0.26-windows-arm64.zip
|
|
@@ -43,9 +43,9 @@ func NewCommand(stdout, stderr io.Writer, runner *common.Runner) *cobra.Command
|
|
|
43
43
|
cmd.SetErr(stderr)
|
|
44
44
|
flags := cmd.Flags()
|
|
45
45
|
flags.StringVar(&opts.Prompt, "prompt", "", "video generation prompt")
|
|
46
|
-
flags.StringArrayVar(&opts.ImagePaths, "image", nil, "local reference image path; repeat for multiple images
|
|
47
|
-
flags.StringArrayVar(&opts.VideoPaths, "video", nil, "local reference video path; repeat for multiple videos
|
|
48
|
-
flags.StringArrayVar(&opts.AudioPaths, "audio", nil, "local reference audio path; repeat for multiple audios
|
|
46
|
+
flags.StringArrayVar(&opts.ImagePaths, "image", nil, "local reference image path; repeat for multiple images")
|
|
47
|
+
flags.StringArrayVar(&opts.VideoPaths, "video", nil, "local reference video path; repeat for multiple videos")
|
|
48
|
+
flags.StringArrayVar(&opts.AudioPaths, "audio", nil, "local reference audio path; repeat for multiple audios")
|
|
49
49
|
flags.IntVar(&durationSec, "duration", 0, "video duration in seconds")
|
|
50
50
|
flags.StringVar(&opts.Ratio, "ratio", "", "video ratio, such as 9:16, 16:9, 3:4, 4:3")
|
|
51
51
|
flags.StringVar(&opts.Model, "model", "", "video model; normal users: Seedance_2.0_mini_lite; VIP-only: seedance2.0_vision, seedance2.0_fast_vision, Seedance_2.0_mini, Seedance_2.5")
|
|
@@ -189,72 +189,74 @@ func TestGenerateVideoRequiresPrompt(t *testing.T) {
|
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
func
|
|
193
|
-
|
|
194
|
-
t.Fatal("server should not receive request when image count is invalid")
|
|
195
|
-
}))
|
|
196
|
-
defer server.Close()
|
|
197
|
-
|
|
198
|
-
var stdout, stderr bytes.Buffer
|
|
199
|
-
root := newTestRootCommand(t, &stdout, &stderr, server.URL)
|
|
200
|
-
args := []string{"generate-video", "--prompt", "x"}
|
|
201
|
-
for _, path := range mediaPaths("image", ".jpg", 10) {
|
|
202
|
-
args = append(args, "--image", path)
|
|
203
|
-
}
|
|
204
|
-
root.SetArgs(args)
|
|
205
|
-
|
|
206
|
-
err := root.Execute()
|
|
207
|
-
if err == nil {
|
|
208
|
-
t.Fatal("Execute() error = nil, want image count validation")
|
|
209
|
-
}
|
|
210
|
-
if !strings.Contains(err.Error(), "参考图片最多支持 9 个,当前传入 10 个") {
|
|
211
|
-
t.Fatalf("error = %q, want image count validation", err)
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
func TestGenerateVideoRejectsTooManyVideos(t *testing.T) {
|
|
216
|
-
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
217
|
-
t.Fatal("server should not receive request when video count is invalid")
|
|
218
|
-
}))
|
|
219
|
-
defer server.Close()
|
|
220
|
-
|
|
221
|
-
var stdout, stderr bytes.Buffer
|
|
222
|
-
root := newTestRootCommand(t, &stdout, &stderr, server.URL)
|
|
192
|
+
func TestGenerateVideoAcceptsReferencesBeyondFormerLimits(t *testing.T) {
|
|
193
|
+
cwd := chdirTemp(t)
|
|
223
194
|
args := []string{"generate-video", "--prompt", "x"}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
195
|
+
var assetIDs []string
|
|
196
|
+
for _, media := range []struct {
|
|
197
|
+
flag string
|
|
198
|
+
ext string
|
|
199
|
+
count int
|
|
200
|
+
}{
|
|
201
|
+
{"image", ".jpg", 10},
|
|
202
|
+
{"video", ".mp4", 4},
|
|
203
|
+
{"audio", ".mp3", 4},
|
|
204
|
+
} {
|
|
205
|
+
for _, name := range mediaPaths(media.flag, media.ext, media.count) {
|
|
206
|
+
path := filepath.Join(cwd, name)
|
|
207
|
+
if err := os.WriteFile(path, []byte("media-data"), 0o644); err != nil {
|
|
208
|
+
t.Fatalf("WriteFile(%s): %v", path, err)
|
|
209
|
+
}
|
|
210
|
+
args = append(args, "--"+media.flag, path)
|
|
211
|
+
assetIDs = append(assetIDs, name)
|
|
212
|
+
}
|
|
235
213
|
}
|
|
236
|
-
}
|
|
237
214
|
|
|
238
|
-
|
|
215
|
+
uploadIndex := 0
|
|
216
|
+
submitted := false
|
|
239
217
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
240
|
-
|
|
218
|
+
switch r.URL.Path {
|
|
219
|
+
case "/api/biz/v1/skill/upload_file":
|
|
220
|
+
if uploadIndex >= len(assetIDs) {
|
|
221
|
+
t.Fatalf("unexpected upload %d", uploadIndex)
|
|
222
|
+
}
|
|
223
|
+
_, _ = w.Write([]byte(`{"ret":"0","data":{"pippit_asset_id":"` + assetIDs[uploadIndex] + `"}}`))
|
|
224
|
+
uploadIndex++
|
|
225
|
+
case "/api/biz/v1/skill/submit_run":
|
|
226
|
+
if uploadIndex != len(assetIDs) {
|
|
227
|
+
t.Fatalf("uploaded %d references, want %d", uploadIndex, len(assetIDs))
|
|
228
|
+
}
|
|
229
|
+
data, err := io.ReadAll(r.Body)
|
|
230
|
+
if err != nil {
|
|
231
|
+
t.Fatalf("read body: %v", err)
|
|
232
|
+
}
|
|
233
|
+
var body map[string]any
|
|
234
|
+
if err := sonic.Unmarshal(data, &body); err != nil {
|
|
235
|
+
t.Fatalf("decode body: %v", err)
|
|
236
|
+
}
|
|
237
|
+
param, ok := body["video_part_tool_param"].(map[string]any)
|
|
238
|
+
if !ok {
|
|
239
|
+
t.Fatalf("video_part_tool_param = %#v, want object", body["video_part_tool_param"])
|
|
240
|
+
}
|
|
241
|
+
assertAssetRefs(t, param["images"], assetIDs[:10])
|
|
242
|
+
assertAssetRefs(t, param["videos"], assetIDs[10:14])
|
|
243
|
+
assertAssetRefs(t, param["audios"], assetIDs[14:])
|
|
244
|
+
submitted = true
|
|
245
|
+
_, _ = w.Write([]byte(`{"ret":"0","data":{"run":{"thread_id":"thread_123","run_id":"run_456"}}}`))
|
|
246
|
+
default:
|
|
247
|
+
t.Fatalf("unexpected path %s", r.URL.Path)
|
|
248
|
+
}
|
|
241
249
|
}))
|
|
242
250
|
defer server.Close()
|
|
243
251
|
|
|
244
252
|
var stdout, stderr bytes.Buffer
|
|
245
253
|
root := newTestRootCommand(t, &stdout, &stderr, server.URL)
|
|
246
|
-
args := []string{"generate-video", "--prompt", "x"}
|
|
247
|
-
for _, path := range mediaPaths("audio", ".mp3", 4) {
|
|
248
|
-
args = append(args, "--audio", path)
|
|
249
|
-
}
|
|
250
254
|
root.SetArgs(args)
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if err == nil {
|
|
254
|
-
t.Fatal("Execute() error = nil, want audio count validation")
|
|
255
|
+
if err := root.Execute(); err != nil {
|
|
256
|
+
t.Fatalf("Execute() error = %v, stderr = %s", err, stderr.String())
|
|
255
257
|
}
|
|
256
|
-
if !
|
|
257
|
-
t.
|
|
258
|
+
if !submitted {
|
|
259
|
+
t.Fatal("generate-video did not submit references")
|
|
258
260
|
}
|
|
259
261
|
}
|
|
260
262
|
|
package/dist/checksums.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
485d9e51092beaf675a872f890fd744460e7afe45b27ec500ab6b5c90ebb6d0c pippit-tool-cli-1.0.26-darwin-amd64.tar.gz
|
|
2
|
+
978cca38261d539ea8ce2bcfd15deb24b4b317f38c084fdde92a5b9ca51f0739 pippit-tool-cli-1.0.26-darwin-arm64.tar.gz
|
|
3
|
+
d772c3fcef2c7b7ff0e6e52d6f10b2ee4cff1cd5fbdff9f08e0bd252ac378e79 pippit-tool-cli-1.0.26-linux-amd64.tar.gz
|
|
4
|
+
b16e7f2e7d9bbfe122eb0895ae6769a00d230ca250717aebb3f9b8eea0c04eae pippit-tool-cli-1.0.26-linux-arm64.tar.gz
|
|
5
|
+
fd05b98a6001602efad98731a4777cb035eea2bea2acbccb2c1d02af0223176e pippit-tool-cli-1.0.26-windows-amd64.zip
|
|
6
|
+
a0c873456cf254b0e452c6e376efec5a425ef044b3a1a671d063fa5a613242d0 pippit-tool-cli-1.0.26-windows-arm64.zip
|
|
@@ -9,12 +9,6 @@ import (
|
|
|
9
9
|
"github.com/Pippit-dev/pippit-cli/internal/common"
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
const (
|
|
13
|
-
maxReferenceImages = 9
|
|
14
|
-
maxReferenceVideos = 3
|
|
15
|
-
maxReferenceAudios = 3
|
|
16
|
-
)
|
|
17
|
-
|
|
18
12
|
var (
|
|
19
13
|
allowedImageExtensionList = []string{".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg"}
|
|
20
14
|
allowedAudioExtensionList = []string{".mp3", ".wav"}
|
|
@@ -70,15 +64,6 @@ func ValidateOptions(opts *Options) error {
|
|
|
70
64
|
if strings.TrimSpace(opts.Prompt) == "" {
|
|
71
65
|
return fmt.Errorf("缺少必填参数 --prompt")
|
|
72
66
|
}
|
|
73
|
-
if len(opts.ImagePaths) > maxReferenceImages {
|
|
74
|
-
return fmt.Errorf("参考图片最多支持 %d 个,当前传入 %d 个", maxReferenceImages, len(opts.ImagePaths))
|
|
75
|
-
}
|
|
76
|
-
if len(opts.VideoPaths) > maxReferenceVideos {
|
|
77
|
-
return fmt.Errorf("参考视频最多支持 %d 个,当前传入 %d 个", maxReferenceVideos, len(opts.VideoPaths))
|
|
78
|
-
}
|
|
79
|
-
if len(opts.AudioPaths) > maxReferenceAudios {
|
|
80
|
-
return fmt.Errorf("参考音频最多支持 %d 个,当前传入 %d 个", maxReferenceAudios, len(opts.AudioPaths))
|
|
81
|
-
}
|
|
82
67
|
if err := validateMediaExtensions("图片", opts.ImagePaths, allowedImageExtensions, allowedImageExtensionList); err != nil {
|
|
83
68
|
return err
|
|
84
69
|
}
|