@pippit-dev/cli 0.0.10 → 0.0.12

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 CHANGED
@@ -10,9 +10,9 @@ matching prebuilt binary for macOS, Linux, or Windows:
10
10
  ```bash
11
11
  npx @pippit-dev/cli@latest install
12
12
  export XYQ_ACCESS_KEY="<access-key>"
13
- pippit-cli version
13
+ pippit-cli --version
14
14
  pippit-cli short-drama +submit-run --message "写一个赛博朋克短剧开头"
15
- pippit-cli short-drama +upload-file --path ./story.md
15
+ pippit-cli short-drama +upload-file --path ./reference.doc
16
16
  pippit-cli short-drama +get-thread --thread-id thread_123 --run-id run_456 --after-seq 0
17
17
  pippit-cli short-drama +download-result --output-path ./thread_123/results/result.mp4 --url URL
18
18
  ```
@@ -24,9 +24,9 @@ Submit a Run task for the short drama scene:
24
24
 
25
25
  ```bash
26
26
  export XYQ_ACCESS_KEY="<access-key>"
27
- go run . version
27
+ go run . --version
28
28
  go run . short-drama +submit-run --message "写一个赛博朋克短剧开头"
29
- go run . short-drama +upload-file --path ./story.md
29
+ go run . short-drama +upload-file --path ./reference.doc
30
30
  go run . short-drama +get-thread --thread-id thread_123 --run-id run_456 --after-seq 0
31
31
  go run . short-drama +download-result --output-path ./thread_123/results/result.mp4 --url URL
32
32
  ```
@@ -35,9 +35,11 @@ go run . short-drama +download-result --output-path ./thread_123/results/result.
35
35
  `run_id`, and `web_thread_link`; `--message` is required.
36
36
  `+get-thread` calls
37
37
  `/api/biz/v1/skill/get_thread` and prints extracted `messages`.
38
+ `+upload-file` calls `/api/biz/v1/skill/upload_file` with
39
+ `multipart/form-data` and prints the returned `asset_id`.
40
+ Only `.doc` and `.txt` file extensions are supported.
38
41
  `+download-result` downloads the result URL to
39
- the `--output-path` file path. `+upload-file` is still mocked while
40
- its real service contract is wired.
42
+ the `--output-path` file path.
41
43
 
42
44
  ## HTTP Client
43
45
 
@@ -47,6 +49,6 @@ settings such as base URL, HTTP timeout, and API paths are loaded by
47
49
 
48
50
  ## Auth
49
51
 
50
- `short-drama +submit-run` and `short-drama +get-thread` authenticate with
52
+ `short-drama +submit-run`, `short-drama +get-thread`, and `short-drama +upload-file` authenticate with
51
53
  `Authorization: Bearer <XYQ_ACCESS_KEY>`. OAuth command code remains available,
52
54
  but runtime short drama requests do not use it.
package/checksums.txt CHANGED
@@ -1,6 +1,6 @@
1
- 5aca1f46d99e5ab9d748cf14d4cfe5fa68b2d2cb248073db0c92d0de361e5fb8 pippit-cli-0.0.10-darwin-amd64.tar.gz
2
- b56de7e3bdd202d449ea5658171b52dbde313c1e3f12f999c583a6b321624d11 pippit-cli-0.0.10-darwin-arm64.tar.gz
3
- b9ddcbd19ffa7aada4c114f7be8b8d6fb870b9a0b9ffed99c0df196ce05658d4 pippit-cli-0.0.10-linux-amd64.tar.gz
4
- 3b8a11becb6229f22c39e2c86caaa897f4ae9147e0960414c0d763ce00bbdd5b pippit-cli-0.0.10-linux-arm64.tar.gz
5
- 26a57f485227f9674591ed20536baa281c443b5ac1c26711e064a77d9ebc6174 pippit-cli-0.0.10-windows-amd64.zip
6
- 8fd4eca3a8977b901d74aa6455293c3c400151e5981491f6d3df1d568781c3eb pippit-cli-0.0.10-windows-arm64.zip
1
+ 5062d1d4950aa9e0d8b1770e62476d9bb2076f4492cd24a541ad93b05a254f49 pippit-cli-0.0.12-darwin-amd64.tar.gz
2
+ 5a0c8d044ebcb717d18954147433ac5f54f2dc89285e2e50f6be6755431ee99c pippit-cli-0.0.12-darwin-arm64.tar.gz
3
+ 456420a71cd9909e152907faa96f35abb93e62079786021d1bee28cfac61e8ff pippit-cli-0.0.12-linux-amd64.tar.gz
4
+ 1c6681fa8f8edaa19dac835dd9bdf4bc17c06936700316f7d74b68d55d572460 pippit-cli-0.0.12-linux-arm64.tar.gz
5
+ e7c156bb35fb373fa89d05c729252b20951a41c70f3bbd4e8794e4c5abe65e06 pippit-cli-0.0.12-windows-amd64.zip
6
+ 2832786f9c13069f3580407e24037e95cc9452534e9996366dc6751f841fcd26 pippit-cli-0.0.12-windows-arm64.zip
package/cmd/root.go CHANGED
@@ -7,7 +7,6 @@ import (
7
7
  // authcmd "github.com/Pippit-dev/pippit-cli/cmd/auth"
8
8
  "github.com/Pippit-dev/pippit-cli/cmd/short_drama"
9
9
  updatecmd "github.com/Pippit-dev/pippit-cli/cmd/update"
10
- versioncmd "github.com/Pippit-dev/pippit-cli/cmd/version"
11
10
  "github.com/Pippit-dev/pippit-cli/internal/common"
12
11
  "github.com/Pippit-dev/pippit-cli/internal/config"
13
12
  "github.com/Pippit-dev/pippit-cli/internal/version"
@@ -30,15 +29,17 @@ func newRootCommand(stdout, stderr io.Writer, runner *common.Runner) *cobra.Comm
30
29
  root := &cobra.Command{
31
30
  Use: "pippit-cli",
32
31
  Short: "Pippit CLI",
32
+ Long: "Pippit CLI submits short-drama workflows, downloads generated assets, and updates the installed CLI package.",
33
33
  Version: version.Current(),
34
34
  SilenceUsage: true,
35
35
  SilenceErrors: true,
36
36
  }
37
+ root.CompletionOptions.DisableDefaultCmd = true
38
+ root.SetVersionTemplate("{{.Version}}\n")
37
39
  root.SetOut(stdout)
38
40
  root.SetErr(stderr)
39
41
  // root.AddCommand(authcmd.NewCommand(stdout, stderr, runner)) // temporarily disabled; auth is via access key injection
40
42
  root.AddCommand(short_drama.NewCommand(stdout, stderr, runner))
41
43
  root.AddCommand(updatecmd.NewCommand(stdout, stderr))
42
- root.AddCommand(versioncmd.NewCommand(stdout))
43
44
  return root
44
45
  }
@@ -3,7 +3,6 @@ package short_drama
3
3
  import (
4
4
  "fmt"
5
5
  "io"
6
- "path/filepath"
7
6
  "strings"
8
7
 
9
8
  "github.com/Pippit-dev/pippit-cli/internal/common"
@@ -72,8 +71,6 @@ func newShortDramaUploadFileCommand(stdout, stderr io.Writer, runner *common.Run
72
71
  if opts.Path == "" {
73
72
  return fmt.Errorf("--path is required")
74
73
  }
75
- opts.FileName = filepath.Base(opts.Path)
76
- opts.Mock = true
77
74
 
78
75
  result, err := common.UploadFile(cmd.Context(), opts, runner)
79
76
  if err != nil {
@@ -91,28 +91,38 @@ func TestRootIncludesUpdateCommand(t *testing.T) {
91
91
  }
92
92
  }
93
93
 
94
- func TestRootIncludesVersionCommand(t *testing.T) {
94
+ func TestRootDoesNotIncludeVersionCommand(t *testing.T) {
95
95
  var stdout, stderr bytes.Buffer
96
96
  root := NewRootCommand(&stdout, &stderr)
97
97
  cmd, _, err := root.Find([]string{"version"})
98
- if err != nil {
99
- t.Fatalf("Find(version) error = %v", err)
100
- }
101
- if cmd == nil || cmd.Name() != "version" {
102
- t.Fatalf("Find(version) = %#v, want version command", cmd)
98
+ if err == nil || cmd != root {
99
+ t.Fatalf("Find(version) = (%#v, %v), want unknown command", cmd, err)
103
100
  }
104
101
  }
105
102
 
106
- func TestVersionCommandPrintsVersion(t *testing.T) {
103
+ func TestRootHelpListsSupportedCommands(t *testing.T) {
107
104
  var stdout, stderr bytes.Buffer
108
105
  root := NewRootCommand(&stdout, &stderr)
109
- root.SetArgs([]string{"version"})
106
+ root.SetArgs([]string{"--help"})
110
107
 
111
108
  if err := root.Execute(); err != nil {
112
109
  t.Fatalf("Execute() error = %v, stderr = %s", err, stderr.String())
113
110
  }
114
- if got := strings.TrimSpace(stdout.String()); got != version.Current() {
115
- t.Fatalf("version output = %q, want %s", got, version.Current())
111
+ got := stdout.String()
112
+ for _, want := range []string{
113
+ "Pippit CLI submits short-drama workflows",
114
+ "short-drama",
115
+ "update",
116
+ "--version",
117
+ } {
118
+ if !strings.Contains(got, want) {
119
+ t.Fatalf("help output = %q, want %q", got, want)
120
+ }
121
+ }
122
+ for _, unwanted := range []string{"completion", "version "} {
123
+ if strings.Contains(got, unwanted) {
124
+ t.Fatalf("help output = %q, should not contain %q", got, unwanted)
125
+ }
116
126
  }
117
127
  }
118
128
 
@@ -124,8 +134,8 @@ func TestVersionFlagPrintsVersion(t *testing.T) {
124
134
  if err := root.Execute(); err != nil {
125
135
  t.Fatalf("Execute() error = %v, stderr = %s", err, stderr.String())
126
136
  }
127
- if got := stdout.String(); !strings.Contains(got, version.Current()) {
128
- t.Fatalf("version flag output = %q, want version", got)
137
+ if got := strings.TrimSpace(stdout.String()); got != version.Current() {
138
+ t.Fatalf("version flag output = %q, want %s", got, version.Current())
129
139
  }
130
140
  }
131
141
 
@@ -166,23 +176,65 @@ func TestShortDramaSubmitRunRequiresAccessKey(t *testing.T) {
166
176
  }
167
177
 
168
178
  func TestShortDramaUploadFile(t *testing.T) {
179
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
180
+ if r.Method != http.MethodPost {
181
+ t.Fatalf("method = %s, want POST", r.Method)
182
+ }
183
+ if r.URL.Path != "/api/biz/v1/skill/upload_file" {
184
+ t.Fatalf("path = %s, want upload_file path", r.URL.Path)
185
+ }
186
+ if r.Header.Get("Authorization") != "Bearer test-token" {
187
+ t.Fatalf("Authorization = %q, want test bearer token", r.Header.Get("Authorization"))
188
+ }
189
+ if err := r.ParseMultipartForm(1 << 20); err != nil {
190
+ t.Fatalf("ParseMultipartForm(): %v", err)
191
+ }
192
+ if got := r.FormValue("accessKey"); got != "" {
193
+ t.Fatalf("accessKey = %q, want empty (auth via header only)", got)
194
+ }
195
+ files := r.MultipartForm.File["file"]
196
+ if len(files) != 1 {
197
+ t.Fatalf("file parts = %d, want 1", len(files))
198
+ }
199
+ if files[0].Filename != "story.txt" {
200
+ t.Fatalf("filename = %q, want story.txt", files[0].Filename)
201
+ }
202
+ if got := files[0].Header.Get("Content-Type"); got != "text/plain; charset=utf-8" {
203
+ t.Fatalf("Content-Type = %q, want text/plain; charset=utf-8", got)
204
+ }
205
+ file, err := files[0].Open()
206
+ if err != nil {
207
+ t.Fatalf("Open multipart file: %v", err)
208
+ }
209
+ defer file.Close()
210
+ data, err := io.ReadAll(file)
211
+ if err != nil {
212
+ t.Fatalf("ReadAll multipart file: %v", err)
213
+ }
214
+ if string(data) != "txt-data" {
215
+ t.Fatalf("file content = %q, want txt-data", string(data))
216
+ }
217
+ _, _ = w.Write([]byte(`{"ret":"0","errmsg":"","data":{"pippit_asset_id":"asset_123"}}`))
218
+ }))
219
+ defer server.Close()
220
+
221
+ cwd := chdirTemp(t)
222
+ path := filepath.Join(cwd, "story.txt")
223
+ if err := os.WriteFile(path, []byte("txt-data"), 0o644); err != nil {
224
+ t.Fatalf("WriteFile(): %v", err)
225
+ }
226
+
169
227
  var stdout, stderr bytes.Buffer
170
- root := NewRootCommand(&stdout, &stderr)
171
- root.SetArgs([]string{"short-drama", "+upload-file", "--path", "/tmp/story.md"})
228
+ root := newTestRootCommand(t, &stdout, &stderr, server.URL)
229
+ root.SetArgs([]string{"short-drama", "+upload-file", "--path", path})
172
230
 
173
231
  if err := root.Execute(); err != nil {
174
232
  t.Fatalf("Execute() error = %v, stderr = %s", err, stderr.String())
175
233
  }
176
234
 
177
235
  got := decodeJSON(t, stdout.Bytes())
178
- if got["scene"] != "short-drama" {
179
- t.Fatalf("scene = %v, want short-drama", got["scene"])
180
- }
181
- if got["status"] != "uploaded" {
182
- t.Fatalf("status = %v, want uploaded", got["status"])
183
- }
184
- if !strings.HasPrefix(got["file_id"].(string), "file_mock_") {
185
- t.Fatalf("file_id = %v, want mock file id", got["file_id"])
236
+ if got["asset_id"] != "asset_123" {
237
+ t.Fatalf("asset_id = %v, want asset_123", got["asset_id"])
186
238
  }
187
239
  }
188
240
 
@@ -200,6 +252,51 @@ func TestShortDramaUploadFileRequiresPath(t *testing.T) {
200
252
  }
201
253
  }
202
254
 
255
+ func TestShortDramaUploadFileRequiresAccessKey(t *testing.T) {
256
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
257
+ t.Fatal("server should not receive request without access key")
258
+ }))
259
+ defer server.Close()
260
+
261
+ cwd := chdirTemp(t)
262
+ path := filepath.Join(cwd, "story.txt")
263
+ if err := os.WriteFile(path, []byte("txt-data"), 0o644); err != nil {
264
+ t.Fatalf("WriteFile(): %v", err)
265
+ }
266
+
267
+ var stdout, stderr bytes.Buffer
268
+ root := newTestRootCommandWithAccessKey(t, &stdout, &stderr, server.URL, "")
269
+ root.SetArgs([]string{"short-drama", "+upload-file", "--path", path})
270
+
271
+ err := root.Execute()
272
+ if err == nil {
273
+ t.Fatal("Execute() error = nil, want access key error")
274
+ }
275
+ if !strings.Contains(err.Error(), "XYQ_ACCESS_KEY is required") {
276
+ t.Fatalf("error = %q, want access key guidance", err)
277
+ }
278
+ }
279
+
280
+ func TestShortDramaUploadFileRejectsUnsupportedFileType(t *testing.T) {
281
+ cwd := chdirTemp(t)
282
+ path := filepath.Join(cwd, "story.png")
283
+ if err := os.WriteFile(path, []byte("png-data"), 0o644); err != nil {
284
+ t.Fatalf("WriteFile(): %v", err)
285
+ }
286
+
287
+ var stdout, stderr bytes.Buffer
288
+ root := NewRootCommand(&stdout, &stderr)
289
+ root.SetArgs([]string{"short-drama", "+upload-file", "--path", path})
290
+
291
+ err := root.Execute()
292
+ if err == nil {
293
+ t.Fatal("Execute() error = nil, want file type validation error")
294
+ }
295
+ if !strings.Contains(err.Error(), "only .doc and .txt uploads are supported") {
296
+ t.Fatalf("error = %q, want unsupported type validation", err)
297
+ }
298
+ }
299
+
203
300
  func TestShortDramaDownloadResult(t *testing.T) {
204
301
  server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
205
302
  if r.Header.Get("User-Agent") != "Pippit-CLI/1.0" {
@@ -5,8 +5,12 @@ import (
5
5
  "context"
6
6
  "fmt"
7
7
  "io"
8
+ "mime/multipart"
8
9
  "net/http"
10
+ "net/textproto"
9
11
  "net/url"
12
+ "os"
13
+ "path/filepath"
10
14
  "reflect"
11
15
  "strings"
12
16
  "time"
@@ -16,12 +20,20 @@ import (
16
20
 
17
21
  type Client interface {
18
22
  SendRequest(ctx context.Context, path string, body any, out any) error
23
+ SendMultipartRequest(ctx context.Context, path string, fields map[string]string, file MultipartFile, out any) error
19
24
  }
20
25
 
21
26
  type RequestAuthorizer interface {
22
27
  Inject(ctx context.Context, req *http.Request) error
23
28
  }
24
29
 
30
+ type MultipartFile struct {
31
+ FieldName string
32
+ Path string
33
+ FileName string
34
+ ContentType string
35
+ }
36
+
25
37
  type httpClient struct {
26
38
  baseURL string
27
39
  httpClient *http.Client
@@ -100,6 +112,89 @@ func (c *httpClient) SendRequest(ctx context.Context, path string, body any, out
100
112
  return c.do(req, out)
101
113
  }
102
114
 
115
+ func (c *httpClient) SendMultipartRequest(ctx context.Context, path string, fields map[string]string, file MultipartFile, out any) error {
116
+ reqURL, err := c.resolveURL(path, nil)
117
+ if err != nil {
118
+ return err
119
+ }
120
+ if file.FieldName == "" {
121
+ return fmt.Errorf("multipart file field name is required")
122
+ }
123
+ if file.FileName == "" {
124
+ file.FileName = filepath.Base(file.Path)
125
+ }
126
+ if file.ContentType == "" {
127
+ file.ContentType = "application/octet-stream"
128
+ }
129
+
130
+ pr, pw := io.Pipe()
131
+ writer := multipart.NewWriter(pw)
132
+ req, err := http.NewRequestWithContext(ctx, http.MethodPost, reqURL, pr)
133
+ if err != nil {
134
+ _ = pr.Close()
135
+ _ = pw.Close()
136
+ return fmt.Errorf("build POST request: %w", err)
137
+ }
138
+ req.Header.Set("Content-Type", writer.FormDataContentType())
139
+ req.Header.Set("Accept", "application/json")
140
+
141
+ if c.authorizer == nil {
142
+ _ = pr.Close()
143
+ _ = pw.Close()
144
+ return fmt.Errorf("authorized request requires authorizer")
145
+ }
146
+ if err := c.authorizer.Inject(ctx, req); err != nil {
147
+ _ = pr.Close()
148
+ _ = pw.Close()
149
+ return fmt.Errorf("inject auth headers: %w", err)
150
+ }
151
+ c.injectHeaders(req)
152
+
153
+ go func() {
154
+ err := writeMultipartBody(writer, fields, file)
155
+ closeErr := writer.Close()
156
+ if err != nil {
157
+ _ = pw.CloseWithError(err)
158
+ return
159
+ }
160
+ _ = pw.CloseWithError(closeErr)
161
+ }()
162
+
163
+ return c.do(req, out)
164
+ }
165
+
166
+ func writeMultipartBody(writer *multipart.Writer, fields map[string]string, file MultipartFile) error {
167
+ for key, value := range fields {
168
+ if err := writer.WriteField(key, value); err != nil {
169
+ return err
170
+ }
171
+ }
172
+
173
+ f, err := os.Open(file.Path)
174
+ if err != nil {
175
+ return fmt.Errorf("open upload file: %w", err)
176
+ }
177
+ defer f.Close()
178
+
179
+ header := make(textproto.MIMEHeader)
180
+ header.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, escapeQuotes(file.FieldName), escapeQuotes(file.FileName)))
181
+ header.Set("Content-Type", file.ContentType)
182
+ part, err := writer.CreatePart(header)
183
+ if err != nil {
184
+ return err
185
+ }
186
+ if _, err := io.Copy(part, f); err != nil {
187
+ return fmt.Errorf("write upload file: %w", err)
188
+ }
189
+ return nil
190
+ }
191
+
192
+ var quotesReplacer = strings.NewReplacer("\\", "\\\\", `"`, `\"`)
193
+
194
+ func escapeQuotes(s string) string {
195
+ return quotesReplacer.Replace(s)
196
+ }
197
+
103
198
  func (c *httpClient) injectHeaders(req *http.Request) {
104
199
  for k, values := range c.headers {
105
200
  for _, v := range values {
@@ -2,48 +2,103 @@ package common
2
2
 
3
3
  import (
4
4
  "context"
5
- "crypto/sha1"
6
- "encoding/hex"
7
- "time"
5
+ "fmt"
6
+ "mime"
7
+ "os"
8
+ "path/filepath"
9
+ "strings"
10
+
11
+ "github.com/Pippit-dev/pippit-cli/internal/config"
8
12
  )
9
13
 
10
14
  // UploadFileOptions is the stable command-facing request shape for file upload.
11
15
  type UploadFileOptions struct {
12
16
  Path string `json:"path"`
13
17
  FileName string `json:"file_name"`
14
- Purpose string `json:"purpose"`
15
- Mock bool `json:"mock"`
16
18
  }
17
19
 
18
20
  // UploadFileResult is the JSON envelope printed by `pippit-cli short-drama +upload-file`.
19
21
  type UploadFileResult struct {
20
- Scene string `json:"scene"`
21
- FileID string `json:"file_id"`
22
- Status string `json:"status"`
23
- Uploaded string `json:"uploaded_at"`
24
- Request UploadFileOptions `json:"request"`
22
+ AssetID string `json:"asset_id"`
23
+ }
24
+
25
+ type uploadFileResponse struct {
26
+ Ret string `json:"ret"`
27
+ Errmsg string `json:"errmsg"`
28
+ SvrTime int64 `json:"svr_time"`
29
+ LogID string `json:"log_id"`
30
+ Data struct {
31
+ PippitAssetID string `json:"pippit_asset_id"`
32
+ AssetID string `json:"asset_id"`
33
+ } `json:"data"`
34
+ }
35
+
36
+ const uploadFileFieldName = "file"
37
+
38
+ var allowedUploadExtensions = map[string]bool{
39
+ ".doc": true,
40
+ ".txt": true,
25
41
  }
26
42
 
27
- func UploadFile(ctx context.Context, opts UploadFileOptions, _ *Runner) (*UploadFileResult, error) {
43
+ func UploadFile(ctx context.Context, opts UploadFileOptions, runner *Runner) (*UploadFileResult, error) {
28
44
  if err := ctx.Err(); err != nil {
29
45
  return nil, err
30
46
  }
31
- key := opts.Purpose + "\x00" + opts.FileName + "\x00" + opts.Path
32
- id := stableID(key, 10)
47
+ if runner == nil || runner.Client == nil {
48
+ return nil, fmt.Errorf("upload_file runner client is required")
49
+ }
50
+
51
+ path := strings.TrimSpace(opts.Path)
52
+ if path == "" {
53
+ return nil, fmt.Errorf("upload file path is required")
54
+ }
55
+ info, err := os.Stat(path)
56
+ if err != nil {
57
+ return nil, fmt.Errorf("stat upload file: %w", err)
58
+ }
59
+ if info.IsDir() {
60
+ return nil, fmt.Errorf("upload path %q is a directory", path)
61
+ }
62
+
63
+ ext := strings.ToLower(filepath.Ext(path))
64
+ if !allowedUploadExtensions[ext] {
65
+ return nil, fmt.Errorf("unsupported file extension %q; only .doc and .txt uploads are supported", ext)
66
+ }
67
+ fileName := filepath.Base(path)
68
+ contentType := mime.TypeByExtension(ext)
69
+
70
+ var resp uploadFileResponse
71
+ if err := runner.Client.SendMultipartRequest(ctx, uploadFilePath(runner), nil, MultipartFile{
72
+ FieldName: uploadFileFieldName,
73
+ Path: path,
74
+ FileName: fileName,
75
+ ContentType: contentType,
76
+ }, &resp); err != nil {
77
+ return nil, fmt.Errorf("upload_file request failed: %w", err)
78
+ }
79
+ if resp.Ret != "0" {
80
+ if resp.Errmsg == "" {
81
+ resp.Errmsg = "unknown error"
82
+ }
83
+ return nil, fmt.Errorf("upload_file failed: ret=%s errmsg=%s", resp.Ret, resp.Errmsg)
84
+ }
85
+
86
+ assetID := strings.TrimSpace(resp.Data.PippitAssetID)
87
+ if assetID == "" {
88
+ assetID = strings.TrimSpace(resp.Data.AssetID)
89
+ }
90
+ if assetID == "" {
91
+ return nil, fmt.Errorf("upload_file response missing pippit_asset_id")
92
+ }
93
+
33
94
  return &UploadFileResult{
34
- Scene: "short-drama",
35
- FileID: "file_mock_" + id,
36
- Status: "uploaded",
37
- Uploaded: time.Now().UTC().Format(time.RFC3339),
38
- Request: opts,
95
+ AssetID: assetID,
39
96
  }, nil
40
97
  }
41
98
 
42
- func stableID(key string, n int) string {
43
- sum := sha1.Sum([]byte(key))
44
- id := hex.EncodeToString(sum[:])
45
- if n > len(id) {
46
- return id
99
+ func uploadFilePath(runner *Runner) string {
100
+ if runner != nil && runner.Config != nil && runner.Config.Paths != nil && runner.Config.Paths.UploadFile != "" {
101
+ return runner.Config.Paths.UploadFile
47
102
  }
48
- return id[:n]
103
+ return config.UploadFilePath
49
104
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pippit-dev/cli",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "Pippit CLI",
5
5
  "bin": {
6
6
  "pippit-cli": "scripts/run.js"
@@ -1,21 +0,0 @@
1
- package versioncmd
2
-
3
- import (
4
- "fmt"
5
- "io"
6
-
7
- "github.com/Pippit-dev/pippit-cli/internal/version"
8
- "github.com/spf13/cobra"
9
- )
10
-
11
- func NewCommand(stdout io.Writer) *cobra.Command {
12
- return &cobra.Command{
13
- Use: "version",
14
- Short: "Print pippit-cli version",
15
- Args: cobra.NoArgs,
16
- RunE: func(cmd *cobra.Command, _ []string) error {
17
- _, err := fmt.Fprintln(stdout, version.Current())
18
- return err
19
- },
20
- }
21
- }