@pippit-dev/cli 0.0.6 → 0.0.8

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/checksums.txt CHANGED
@@ -1,6 +1,6 @@
1
- 2d6ebc213dd8401622a5315846a1f2125b321ca55ed3689b661268ae0cdc7bb4 pippit-cli-0.0.6-darwin-amd64.tar.gz
2
- 2bedf7ed2f9afd313e8b983763e90e66a835b2f340fcda905cae8c82ba553735 pippit-cli-0.0.6-darwin-arm64.tar.gz
3
- 37ecf71b5d701dc257c0287983b511e403ff0cfc7690a07a0c4bcd32837c365e pippit-cli-0.0.6-linux-amd64.tar.gz
4
- b6e41054960723d18809e1bad498f59fb7f68550e07c11e867f9580ac91a42da pippit-cli-0.0.6-linux-arm64.tar.gz
5
- 2103cb871134ec47f34b53b53e93d95d7190888bc035b8c78d48b925a3ace9eb pippit-cli-0.0.6-windows-amd64.zip
6
- 263fbabede0ad9c266ef23cdad2292297a35b67d2301c14f400308198bab4655 pippit-cli-0.0.6-windows-arm64.zip
1
+ d403f987a5f3cf416d0d183d0b7f45c6cd13ac36da37ca29480e8c9b53af75ea pippit-cli-0.0.8-darwin-amd64.tar.gz
2
+ 69aa116a8dd6086f00769ac71aca9cb83c5ffe058187c42c212d35aadb06d817 pippit-cli-0.0.8-darwin-arm64.tar.gz
3
+ cdb856501d5472f226bf061073aa2d2af2f61b28151ca908e48e1acc8d1261b4 pippit-cli-0.0.8-linux-amd64.tar.gz
4
+ 6a8013ca271df7ddebba3ffcc4ed76ef79404ad6105893cda7db2969a5f51595 pippit-cli-0.0.8-linux-arm64.tar.gz
5
+ 1fff575ecbef2d8ed5cc5c05b87d08dbad6f68b3aa1c5da13957cac9f887b4ce pippit-cli-0.0.8-windows-amd64.zip
6
+ bfe3160bad9d536a7510486233cbe4bada0581996b142812edcabb741cc5d2de pippit-cli-0.0.8-windows-arm64.zip
@@ -164,8 +164,8 @@ func newShortDramaListThreadFileCommand(stdout, stderr io.Writer, runner *common
164
164
  if opts.ThreadID == "" {
165
165
  return fmt.Errorf("--thread-id is required")
166
166
  }
167
- if opts.PageSize <= 0 || opts.PageSize > 100 {
168
- return fmt.Errorf("--page-size must be between 1 and 100")
167
+ if opts.PageSize <= 0 || opts.PageSize > common.MaxListThreadFilePageSize {
168
+ return fmt.Errorf("--page-size must be between 1 and %d", common.MaxListThreadFilePageSize)
169
169
  }
170
170
  if opts.PageNum <= 0 {
171
171
  return fmt.Errorf("--page-num must be greater than 0")
@@ -182,7 +182,7 @@ func newShortDramaListThreadFileCommand(stdout, stderr io.Writer, runner *common
182
182
  cmd.SetErr(stderr)
183
183
  cmd.Flags().StringVar(&opts.ThreadID, "thread-id", "", "thread ID to list files for")
184
184
  cmd.Flags().IntVar(&opts.PageNum, "page-num", 1, "page number (1-based)")
185
- cmd.Flags().IntVar(&opts.PageSize, "page-size", 100, "number of files per page (between 1 and 100)")
185
+ cmd.Flags().IntVar(&opts.PageSize, "page-size", common.MaxListThreadFilePageSize, fmt.Sprintf("number of files per page (between 1 and %d)", common.MaxListThreadFilePageSize))
186
186
  return cmd
187
187
  }
188
188
 
@@ -194,8 +194,8 @@ func TestShortDramaDownloadResult(t *testing.T) {
194
194
  if got["output_path"] != outputPath {
195
195
  t.Fatalf("output_path = %v, want %s", got["output_path"], outputPath)
196
196
  }
197
- if got["total"] != float64(1) {
198
- t.Fatalf("total = %v, want 1", got["total"])
197
+ if _, ok := got["total"]; ok {
198
+ t.Fatalf("total should not be returned: %#v", got)
199
199
  }
200
200
  downloaded, ok := got["downloaded"].([]any)
201
201
  if !ok || len(downloaded) != 1 {
@@ -248,8 +248,8 @@ func TestShortDramaDownloadResultSkipsExistingFile(t *testing.T) {
248
248
  if got["output_path"] != outputPath {
249
249
  t.Fatalf("output_path = %v, want %s", got["output_path"], outputPath)
250
250
  }
251
- if got["total"] != float64(0) {
252
- t.Fatalf("total = %v, want 0", got["total"])
251
+ if _, ok := got["total"]; ok {
252
+ t.Fatalf("total should not be returned: %#v", got)
253
253
  }
254
254
  alreadyExist, ok := got["already_exist"].([]any)
255
255
  if !ok || len(alreadyExist) != 1 || alreadyExist[0] != outputPath {
@@ -258,6 +258,44 @@ func TestShortDramaDownloadResultSkipsExistingFile(t *testing.T) {
258
258
  assertFileContent(t, outputPath, "existing-data")
259
259
  }
260
260
 
261
+ func TestShortDramaDownloadResultDownloadsMetaJSON(t *testing.T) {
262
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
263
+ if r.URL.Path != "/meta.json" {
264
+ t.Fatalf("path = %s, want meta.json", r.URL.Path)
265
+ }
266
+ _, _ = w.Write([]byte(`{"ok":true}`))
267
+ }))
268
+ defer server.Close()
269
+
270
+ chdirTemp(t)
271
+ outputPath := filepath.Join("results", "meta.json")
272
+
273
+ var stdout, stderr bytes.Buffer
274
+ root := NewRootCommand(&stdout, &stderr)
275
+ root.SetArgs([]string{
276
+ "short-drama", "+download-result",
277
+ "--output-path", outputPath,
278
+ "--url", server.URL + "/meta.json",
279
+ })
280
+
281
+ if err := root.Execute(); err != nil {
282
+ t.Fatalf("Execute() error = %v, stderr = %s", err, stderr.String())
283
+ }
284
+
285
+ got := decodeJSON(t, stdout.Bytes())
286
+ if got["output_path"] != outputPath {
287
+ t.Fatalf("output_path = %v, want %s", got["output_path"], outputPath)
288
+ }
289
+ if _, ok := got["total"]; ok {
290
+ t.Fatalf("total should not be returned: %#v", got)
291
+ }
292
+ downloaded, ok := got["downloaded"].([]any)
293
+ if !ok || len(downloaded) != 1 || downloaded[0] != outputPath {
294
+ t.Fatalf("downloaded = %#v, want meta.json output path", got["downloaded"])
295
+ }
296
+ assertFileContent(t, outputPath, `{"ok":true}`)
297
+ }
298
+
261
299
  func TestShortDramaDownloadResultRequiresOutputPath(t *testing.T) {
262
300
  var stdout, stderr bytes.Buffer
263
301
  root := NewRootCommand(&stdout, &stderr)
@@ -515,6 +553,10 @@ func TestShortDramaListThreadFile(t *testing.T) {
515
553
  if got["total"] != float64(1) {
516
554
  t.Fatalf("total = %v, want 1", got["total"])
517
555
  }
556
+ wantMessage := "<system-remind>\n- total is below 200; continue querying with current --page-num 2\n</system-remind>"
557
+ if got["message"] != wantMessage {
558
+ t.Fatalf("message = %v, want current page hint", got["message"])
559
+ }
518
560
  files, ok := got["files"].([]any)
519
561
  if !ok || len(files) != 1 {
520
562
  t.Fatalf("files = %#v, want one file", got["files"])
@@ -535,6 +577,35 @@ func TestShortDramaListThreadFile(t *testing.T) {
535
577
  }
536
578
  }
537
579
 
580
+ func TestShortDramaListThreadFileMessageWhenPageFull(t *testing.T) {
581
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
582
+ if !strings.Contains(r.URL.Path, "list_thread_file") {
583
+ t.Fatalf("path = %s, want list_thread_file path", r.URL.Path)
584
+ }
585
+ _, _ = w.Write([]byte(`{"ret":"0","errmsg":"","data":{"total":200,"files":[]}}`))
586
+ }))
587
+ defer server.Close()
588
+
589
+ var stdout, stderr bytes.Buffer
590
+ root := newTestRootCommand(t, &stdout, &stderr, server.URL)
591
+ root.SetArgs([]string{
592
+ "short-drama", "+list-thread-file",
593
+ "--thread-id", "thread_123",
594
+ "--page-num", "2",
595
+ "--page-size", "200",
596
+ })
597
+
598
+ if err := root.Execute(); err != nil {
599
+ t.Fatalf("Execute() error = %v, stderr = %s", err, stderr.String())
600
+ }
601
+
602
+ got := decodeJSON(t, stdout.Bytes())
603
+ wantMessage := "<system-remind>\n- total reached 200; query the next page with --page-num 3\n</system-remind>"
604
+ if got["message"] != wantMessage {
605
+ t.Fatalf("message = %v, want next page hint", got["message"])
606
+ }
607
+ }
608
+
538
609
  func TestShortDramaListThreadFileRequiresThreadID(t *testing.T) {
539
610
  var stdout, stderr bytes.Buffer
540
611
  root := NewRootCommand(&stdout, &stderr)
@@ -549,6 +620,24 @@ func TestShortDramaListThreadFileRequiresThreadID(t *testing.T) {
549
620
  }
550
621
  }
551
622
 
623
+ func TestShortDramaListThreadFileRejectsPageSizeAboveMax(t *testing.T) {
624
+ var stdout, stderr bytes.Buffer
625
+ root := NewRootCommand(&stdout, &stderr)
626
+ root.SetArgs([]string{
627
+ "short-drama", "+list-thread-file",
628
+ "--thread-id", "thread_123",
629
+ "--page-size", "201",
630
+ })
631
+
632
+ err := root.Execute()
633
+ if err == nil {
634
+ t.Fatal("Execute() error = nil, want validation error")
635
+ }
636
+ if !strings.Contains(err.Error(), "--page-size must be between 1 and 200") {
637
+ t.Fatalf("error = %q, want page-size validation", err)
638
+ }
639
+ }
640
+
552
641
  func newTestRootCommand(t *testing.T, stdout, stderr io.Writer, baseURL string) *cobra.Command {
553
642
  t.Helper()
554
643
  return newTestRootCommandWithAccessKey(t, stdout, stderr, baseURL, "test-token")
@@ -36,7 +36,6 @@ type DownloadResultResponse struct {
36
36
  OutputPath string `json:"output_path"`
37
37
  Downloaded []string `json:"downloaded"`
38
38
  AlreadyExist []string `json:"already_exist,omitempty"`
39
- Total int `json:"total"`
40
39
  Errors []*DownloadResultError `json:"errors,omitempty"`
41
40
  }
42
41
 
@@ -76,7 +75,6 @@ func DownloadResult(ctx context.Context, opts DownloadResultOptions, runner *Run
76
75
  return &DownloadResultResponse{
77
76
  OutputPath: outputPath,
78
77
  AlreadyExist: []string{outputPath},
79
- Total: 0,
80
78
  }, nil
81
79
  } else if !os.IsNotExist(err) {
82
80
  return nil, fmt.Errorf("stat output path: %w", err)
@@ -161,7 +159,6 @@ func DownloadResult(ctx context.Context, opts DownloadResultOptions, runner *Run
161
159
  result := &DownloadResultResponse{
162
160
  OutputPath: outputPath,
163
161
  Downloaded: downloaded,
164
- Total: len(downloaded),
165
162
  Errors: errorList,
166
163
  }
167
164
  if len(downloaded) == 0 && len(errorList) > 0 {
@@ -9,6 +9,8 @@ import (
9
9
  "github.com/Pippit-dev/pippit-cli/internal/config"
10
10
  )
11
11
 
12
+ const MaxListThreadFilePageSize = 200
13
+
12
14
  // ListThreadFileOptions is the command-facing request shape for listing thread files.
13
15
  type ListThreadFileOptions struct {
14
16
  ThreadID string `json:"thread_id"`
@@ -24,8 +26,9 @@ type ThreadFile struct {
24
26
 
25
27
  // ListThreadFileResult is the JSON envelope printed by `pippit-cli short-drama +list-thread-file`.
26
28
  type ListThreadFileResult struct {
27
- Files []*ThreadFile `json:"files"`
28
- Total int64 `json:"total"`
29
+ Files []*ThreadFile `json:"files"`
30
+ Total int64 `json:"total"`
31
+ Message string `json:"message"`
29
32
  }
30
33
 
31
34
  type listThreadFileResponse struct {
@@ -82,11 +85,26 @@ func ListThreadFile(ctx context.Context, opts *ListThreadFileOptions, runner *Ru
82
85
  }
83
86
 
84
87
  return &ListThreadFileResult{
85
- Files: files,
86
- Total: resp.Data.Total,
88
+ Files: files,
89
+ Total: resp.Data.Total,
90
+ Message: listThreadFileMessage(resp.Data.Total, opts.PageNum),
87
91
  }, nil
88
92
  }
89
93
 
94
+ func listThreadFileMessage(total int64, pageNum int) string {
95
+ start, end := "<system-remind>", "</system-remind>"
96
+ if pageNum <= 0 {
97
+ pageNum = 1
98
+ }
99
+ var message string
100
+ if total >= MaxListThreadFilePageSize {
101
+ message = fmt.Sprintf("total reached %d; query the next page with --page-num %d", MaxListThreadFilePageSize, pageNum+1)
102
+ } else {
103
+ message = fmt.Sprintf("total is below %d; continue querying with current --page-num %d", MaxListThreadFilePageSize, pageNum)
104
+ }
105
+ return fmt.Sprintf("%s\n- %s\n%s", start, message, end)
106
+ }
107
+
90
108
  func threadFilePath(threadID string, filePath string) string {
91
109
  parts := []string{strings.TrimSpace(threadID)}
92
110
  trimmedFilePath := strings.Trim(strings.TrimSpace(filePath), `/\`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pippit-dev/cli",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Pippit CLI",
5
5
  "bin": {
6
6
  "pippit-cli": "scripts/run.js"
@@ -17,17 +17,19 @@ metadata:
17
17
 
18
18
  # 小云雀短剧创作
19
19
 
20
- 通过 `pippit-cli short-drama` 命令提交短剧创作任务、上传参考文件,并查询任务进展,获取会话产物文件。
20
+ 通过 `pippit-cli short-drama` 命令提交短剧创作任务、上传参考文件,并行查询任务进展和会话产物文件,及时把重要资产下载到用户本地。
21
21
 
22
- 短剧场景面向剧情、人物、分集与画面化叙事创作,用户的原始需求通过 `--message` 发送给后端 Agent。后端 Agent 负责理解任务、编排流程和生成内容;用户侧 Agent 只负责提交任务、查询进展和展示结果。
22
+ 短剧场景面向剧情、人物、分集与画面化叙事创作,用户的原始需求通过 `--message` 发送给后端 Agent。后端 Agent 负责理解任务、编排流程和生成内容;用户侧 Agent 负责提交任务、并行查询进展与产物、主动下载重要资产并展示结果。
23
23
 
24
24
  ## 功能
25
25
 
26
26
  1. **提交短剧 Run 任务** - 创建新会话或向已有会话发送短剧创作需求。
27
27
  2. **查询会话进展** - 根据 `thread_id`、`run_id`、`after_seq` 拉取短剧任务消息列表。
28
28
  3. **上传文件** - 上传短剧相关参考文件,得到文件 ID,供后续任务引用。
29
- 4. **获取会话文件** - 根据 `thread_id` 拉取会话文件列表,得到 `file_path`、`file_name`、`download_url`。
30
- 5. **下载文件资源** - 使用文件列表中的 `download_url` 下载资源,并按 `file_path` 写入用户本地目标文件路径。
29
+ 4. **获取会话文件** - 根据 `thread_id` 拉取会话文件列表,得到 `file_path`、`download_url`。这和查询会话进展同等重要。
30
+ 5. **下载重要资产** - 使用文件列表中的 `download_url` 下载资源,并按 `file_path` 写入用户本地目标文件路径。
31
+
32
+ 重要资产包括但不限于:剧本设计、场景设计、场景图、人物角色设计、人物图、分集草稿、故事板、最终视频产物。只要 `+list-thread-file` 返回了这些资产的 `download_url`,就要及时调用下载工具落盘,不要只展示文件元信息。
31
33
 
32
34
  ## 前置要求
33
35
 
@@ -73,7 +75,7 @@ pippit-cli short-drama +upload-file --path /path/to/outline.md
73
75
 
74
76
  ```bash
75
77
  # 获取会话文件列表
76
- pippit-cli short-drama +list-thread-file --thread-id THREAD_ID --page-num 1 --page-size 100
78
+ pippit-cli short-drama +list-thread-file --thread-id THREAD_ID --page-num 1 --page-size 200
77
79
  ```
78
80
 
79
81
  `+list-thread-file` 返回的每个文件对象包含:
@@ -85,7 +87,7 @@ pippit-cli short-drama +list-thread-file --thread-id THREAD_ID --page-num 1 --pa
85
87
  }
86
88
  ```
87
89
 
88
- `+list-thread-file` 只负责获取会话文件列表,不负责下载文件。
90
+ `+list-thread-file` 只负责获取会话文件列表,不负责下载文件,也不需要判断本地文件是否已存在。
89
91
 
90
92
  ### 5. 下载文件资源
91
93
 
@@ -104,20 +106,25 @@ pippit-cli short-drama +download-result --url DOWNLOAD_URL --output-path FILE_PA
104
106
  1. pippit-cli short-drama +submit-run --message "用户的原始短剧需求"
105
107
  → 拿到 thread_id、run_id 和 web_thread_link
106
108
  2. 立即将 web_thread_link 展示给用户
107
- 3. 并行发起:
109
+ 3. 并行发起,二者同等重要:
108
110
  a. pippit-cli short-drama +get-thread --thread-id THREAD_ID --run-id RUN_ID --after-seq SEQUENCE
109
- b. pippit-cli short-drama +list-thread-file --thread-id THREAD_ID --page-num 1 --page-size 100
111
+ b. pippit-cli short-drama +list-thread-file --thread-id THREAD_ID --page-num PAGE_NUM --page-size 200
110
112
  4. 检查 `get-thread` 返回的 messages:
111
113
  - 如果任务仍在进行中:展示过程消息,继续查询
112
114
  - 如果后端 Agent 提出问题:展示问题,等待用户回复
113
- 5. 解析 `list-thread-file` 返回的 files,只获取文件元信息:
114
- - 对每个文件取 file_path、file_name、download_url
115
+ 5. 检查 `list-thread-file` 返回的 files
116
+ - 对每个文件取 file_path、download_url
115
117
  - 将 file_path 作为本地目标文件路径,包含文件名
116
- - 如果 file_path 已存在:跳过下载
117
- 6. 对缺失的本地文件,调用 +download-result 并行下载资源:
118
+ - download_url 的重要资产:加入本轮下载队列
119
+ - 不判断 file_path 在本地是否已存在,是否跳过由 +download-result 内部处理
120
+ - 如果本轮 total 达到 200:下一轮将 PAGE_NUM 加 1,继续查询新一页文件
121
+ 6. 对重要资产,立即调用 +download-result 并行下载资源:
118
122
  - 使用第 5 步获取的 download_url 作为 --url
119
123
  - 使用第 5 步获取的完整 file_path 作为 --output-path
120
- 7. 如用户继续追加需求,使用同一 thread_id 再次 submit-run
124
+ - 剧本设计、场景设计、场景图、人物角色设计、人物图、最终视频产物都属于重要资产
125
+ 7. 查询或下载失败时,不要直接放弃;记录失败项,并在后续轮询中主动重试
126
+ 8. 只有会话进展已处理,且已发现的重要资产均已下载或明确重试失败后,才向用户汇总最终结果
127
+ 9. 如用户继续追加需求,使用同一 thread_id 再次 submit-run
121
128
  ```
122
129
 
123
130
  ### 场景 2:用户提供参考文件要求创作
@@ -127,7 +134,7 @@ pippit-cli short-drama +download-result --url DOWNLOAD_URL --output-path FILE_PA
127
134
  → 拿到 file_id
128
135
  2. pippit-cli short-drama +submit-run --message "用户的原始短剧需求" --asset-ids file_id
129
136
  → 拿到 thread_id、run_id 和 web_thread_link
130
- 3. 后续同场景 1 的并行查询和文件下载流程
137
+ 3. 后续同场景 1 的并行查询、重要资产发现和文件下载流程
131
138
  ```
132
139
 
133
140
  ### 场景 3:在已有短剧会话中续写或修改
@@ -135,18 +142,30 @@ pippit-cli short-drama +download-result --url DOWNLOAD_URL --output-path FILE_PA
135
142
  ```
136
143
  1. pippit-cli short-drama +submit-run --message "用户的新需求" --thread-id THREAD_ID
137
144
  → 拿到新的 run_id 和 web_thread_link
138
- 2. 继续按场景 1 展示进展、处理用户补充问题、获取新增会话文件列表,并按需下载新增文件资源
145
+ 2. 继续按场景 1 展示进展、处理用户补充问题、获取新增会话文件列表,并及时下载新增重要资产
139
146
  ```
140
147
 
141
148
  ## 轮询策略
142
149
 
143
150
  - **间隔**:每 10 秒查询一次。
144
151
  - **增量拉取**:首次使用 `--after-seq 0`,后续根据已读消息进度调整 `after-seq`。
145
- - **并行查询**:每次 `+submit-run` 返回 `thread_id` 后,同时发起 `+get-thread` 和 `+list-thread-file`;会话信息展示流程保持不变,`+list-thread-file` 只用于获取文件元信息。
146
- - **文件下载**:解析 `+list-thread-file` 的结果后,只有目标本地文件不存在时才调用 `+download-result` 下载资源。
152
+ - **并行查询**:每次 `+submit-run` 返回 `thread_id` 后,同时发起 `+get-thread` 和 `+list-thread-file`;二者同等重要,不能只查询会话进展而忽略会话文件。
153
+ - **文件分页**:`+list-thread-file` 使用 `--page-size 200`。如果本轮返回的 `total` 达到 200,下一轮使用 `--page-num` 加 1 查询新一页结果;如果未达到 200,保持当前页继续轮询新增产物。
154
+ - **重要资产识别**:每轮都检查 `+list-thread-file` 返回的文件。剧本设计、场景设计、场景图、人物角色设计、人物图、分集草稿、故事板、最终视频产物都是重要资产。
155
+ - **文件下载**:解析 `+list-thread-file` 的结果后,对带 `download_url` 的重要资产立即调用 `+download-result` 下载资源;不要在 `list-thread-file` 阶段检查文件是否已存在,存在性检查由下载工具内部处理。
156
+ - **下载完成标准**:不要把文件元信息展示当成下载完成;必须拿到本地 `file_path`,或明确记录该文件在重试后仍下载失败。
147
157
  - **用户确认**:如果消息中出现需要用户确认、补充设定或回答问题的内容,先展示给用户,等待用户回复。
148
158
  - **超时**:如果长时间无结果,告知用户任务仍在生成中,可稍后通过 `web_thread_link` 查看。
149
- - **错误处理**:单次查询失败可重试;连续失败时停止轮询并向用户说明错误。
159
+ - **错误处理**:`+get-thread`、`+list-thread-file` 或 `+download-result` 任一调用失败时,记录失败原因和参数,在后续轮询中主动重试;重试期间继续处理其他成功返回的消息和文件。连续多轮失败后再向用户说明仍未完成的查询或下载项。
160
+
161
+ ## 完成标准
162
+
163
+ 一次短剧任务不能只以 `+get-thread` 返回完成消息作为结束条件。完成前必须同时检查:
164
+
165
+ 1. 已处理 `+get-thread` 返回的最新会话进展、用户确认问题和最终消息。
166
+ 2. 已用 `--page-size 200` 调用 `+list-thread-file` 获取会话文件列表;如果本轮 `total` 达到 200,已在后续轮询中递增 `page-num` 查询新一页。
167
+ 3. 对所有带 `download_url` 的重要资产,已调用 `+download-result` 下载到本地 `file_path`。
168
+ 4. 对查询失败或下载失败的资产,已在后续轮询中主动重试,并在最终回复中列出仍失败的文件或命令。
150
169
 
151
170
  ## 输出格式
152
171
 
@@ -204,51 +223,59 @@ pippit-cli short-drama +download-result --url DOWNLOAD_URL --output-path FILE_PA
204
223
  "download_url": "https://..."
205
224
  }
206
225
  ],
207
- "total": 1
226
+ "total": 1,
227
+ "message": "<system-remind>\n- total reached 200; query the next page with --page-num {page-num} + 1\n</system-remind>"
208
228
  }
209
229
  ```
210
230
 
231
+ 当 `total` 达到 200 时,`message` 会用 `<system-remind>` 提示下一轮将 `page-num` 加 1 查询新一页。
232
+
211
233
  **+download-result** 返回:
212
234
 
213
235
  ```json
214
236
  {
215
237
  "output_path": "./{thread-id}/{file_path}/{file_name}",
216
- "downloaded": ["./{thread-id}/{file_path}/{file_name}"],
217
- "total": 1
238
+ "downloaded": ["./{thread-id}/{file_path}/{file_name}"]
218
239
  }
219
240
  ```
220
241
 
221
242
  ## 会话文件与资源下载
222
243
 
223
- 先用 `+list-thread-file` 获取会话文件列表,再按需用 `+download-result` 并行下载文件资源。
244
+ 先用 `+list-thread-file` 获取会话文件列表,再用 `+download-result` 并行下载重要资产。获取文件元信息不是最终目标,重要资产落盘才是核心目标。文件是否已存在由下载工具内部检查,`+list-thread-file` 阶段不要做本地存在性判断。
224
245
 
225
246
  ### 获取会话文件
226
247
 
227
- 从 `+list-thread-file` 的 `files` 中逐个读取文件元信息:`file_path`、`download_url`。
248
+ 从 `+list-thread-file` 的 `files` 中逐个读取文件元信息:`file_path`、`file_name`、`download_url`。重点识别剧本设计、场景设计、场景图、人物角色设计、人物图、分集草稿、故事板、最终视频产物等重要资产。
228
249
 
229
250
  ```
230
- 1. file_path在本地已存在
231
- → 跳过
232
- 2. file_path在本地不存在
251
+ 1. 有download_url的重要资产
233
252
  → 记录该file_path和URL
234
253
  → 使用 +download-result 将URL资源下载到该file_path
254
+ 2. 本轮total达到200
255
+ → 下一轮page-num加1,继续查询新一页结果
256
+ 3. 本轮total未达到200
257
+ → 后续轮询保持当前页,继续发现新增产物
258
+ 4. list-thread-file或download-result失败
259
+ → 记录失败参数和错误
260
+ → 后续轮询主动重试,不要直接结束任务
235
261
  ```
236
262
 
237
263
  ### 并行下载文件资源
238
264
 
239
- 只对目标路径不存在的文件调用下载工具,可并行:
265
+ 对带 `download_url` 的重要资产调用下载工具,可并行。重要资产必须主动下载,不要等用户再次要求,也不要在调用下载工具前先检查本地文件是否存在。
240
266
 
241
267
  1. 调用 `pippit-cli short-drama +download-result --url DOWNLOAD_URL --output-path FILE_PATH`。
242
- 2. 下载完成后,向用户展示本地文件路径;如果某个文件下载失败,只报告该文件错误,不阻塞已成功落盘的文件展示。
268
+ 2. 下载完成后,向用户展示本地文件路径;如果某个文件下载失败,记录失败项并在后续轮询中重试,不阻塞已成功落盘的文件展示。
243
269
 
244
270
  ## 向用户展示内容
245
271
 
246
272
  - 任务提交后:立即展示 `web_thread_link`。
247
273
  - 任务进行中:展示后端 Agent 返回的过程消息。
248
274
  - 需要用户补充信息时:原样展示后端 Agent 的问题,等待用户回复。
249
- - 任务完成后:展示短剧内容、分集草稿、设定说明或其他结果信息。
275
+ - 任务完成后:展示短剧内容、分集草稿、设定说明或其他结果信息,同时检查是否有未下载的重要资产。
250
276
  - 获取会话文件后:展示或记录文件元信息,不把它当成已下载结果。
251
277
  - 文件资源下载后:展示已落盘的本地文件路径;已存在而跳过下载的文件也要标明。
278
+ - 如果仍有重要资产下载失败:说明失败文件、失败命令和已进行的重试,不要把它描述为已完成下载。
252
279
 
253
280
  ## 核心原则:用户侧不做创作,只做传话
254
281
 
@@ -275,6 +302,6 @@ pippit-cli short-drama +download-result --url DOWNLOAD_URL --output-path FILE_PA
275
302
  - 查询进展时优先使用 `+submit-run` 返回的 `thread_id` 和 `run_id`。
276
303
  - `--after-seq` 用于增量拉取消息,首次查询可设置为 `0`。
277
304
  - `+upload-file` 当前用于短剧场景文件上传链路,上传后将返回可传给 `+submit-run` 的文件 ID。
278
- - `+list-thread-file` 只需要 `thread_id`;分页参数默认 `--page-num 1 --page-size 100`。
305
+ - `+list-thread-file` 只需要 `thread_id`;分页参数使用 `--page-num 1 --page-size 200` 起步,`total` 达到 200 时下一轮递增 `page-num`。
279
306
  - `+list-thread-file` 和 `+download-result` 是两个不同的 CLI 指令:前者获取会话文件元信息,后者下载 URL 资源并写入到本地目标文件路径。
280
307
  - `+download-result` 接收 `--url`、`--output-path`、`--workers`;`--output-path` 必须是包含文件名的目标文件路径。