@pippit-dev/cli 1.0.21 → 1.0.23
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 +118 -41
- package/checksums.txt +6 -6
- package/cmd/canvas/canvas.go +2 -0
- package/cmd/canvas/canvas_test.go +2 -2
- package/cmd/canvas/command_help.go +40 -0
- package/cmd/root.go +6 -3
- package/cmd/root_test.go +38 -0
- package/cmd/submit_run.go +40 -0
- package/cmd/submit_run_test.go +103 -0
- package/cmd/upload_file.go +64 -0
- package/cmd/upload_file_test.go +155 -0
- package/dist/checksums.txt +6 -6
- package/dist/xyq-canvas-command-runtime.cjs +14 -14
- package/dist/xyq-canvas-command-runtime.cjs.LEGAL.txt +235 -3
- package/dist/xyq-canvas-command-runtime.cjs.sha256 +2 -2
- package/internal/common/upload_file.go +1 -1
- package/internal/config/config_test.go +10 -0
- package/package.json +8 -3
- package/scripts/canvas-command-discovery.js +202 -0
- package/scripts/canvas-command-guides.js +171 -0
- package/scripts/canvas-command.js +100 -23
- package/scripts/install-cli.js +19 -0
- package/scripts/install-wizard.js +46 -22
- package/scripts/install.js +8 -6
- package/scripts/run.js +58 -42
- package/skills/xyq-nest-skill/SKILL.md +113 -60
- package/skills/xyq-nest-skill/scripts/ensure-cli.js +149 -0
- package/skills/xyq-nest-skill/scripts/get_thread.py +1 -1
- package/skills/xyq-nest-skill/scripts/xyq_common.py +34 -31
- package/skills/xyq-nest-skill/scripts/download_results.py +0 -105
- package/skills/xyq-nest-skill/scripts/submit_run.py +0 -76
- package/skills/xyq-nest-skill/scripts/upload_file.py +0 -133
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
package cmd
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"io"
|
|
6
|
+
"net/http"
|
|
7
|
+
"net/http/httptest"
|
|
8
|
+
"os"
|
|
9
|
+
"path/filepath"
|
|
10
|
+
"strings"
|
|
11
|
+
"testing"
|
|
12
|
+
|
|
13
|
+
"github.com/Pippit-dev/pippit-cli/internal/config"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
func TestUploadFileMediaContract(t *testing.T) {
|
|
17
|
+
for _, name := range []string{"image.png", "image.WEBP", "video.mp4", "audio.mp3", "audio.WAV"} {
|
|
18
|
+
t.Run(name, func(t *testing.T) {
|
|
19
|
+
path := filepath.Join(t.TempDir(), name)
|
|
20
|
+
if err := os.WriteFile(path, []byte("media-content"), 0o600); err != nil {
|
|
21
|
+
t.Fatal(err)
|
|
22
|
+
}
|
|
23
|
+
calls := 0
|
|
24
|
+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
25
|
+
calls++
|
|
26
|
+
if r.Method != http.MethodPost || r.URL.Path != config.UploadFilePath {
|
|
27
|
+
t.Errorf("unexpected request: %s %s", r.Method, r.URL.Path)
|
|
28
|
+
}
|
|
29
|
+
if r.Header.Get("Authorization") != "Bearer test-token" {
|
|
30
|
+
t.Error("missing bearer authorization")
|
|
31
|
+
}
|
|
32
|
+
body, err := io.ReadAll(r.Body)
|
|
33
|
+
if err != nil {
|
|
34
|
+
t.Error(err)
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
if bytes.Contains(body, []byte("test-token")) || bytes.Contains(body, []byte(`name="accessKey"`)) {
|
|
38
|
+
t.Error("credentials must not appear in multipart body")
|
|
39
|
+
}
|
|
40
|
+
r.Body = io.NopCloser(bytes.NewReader(body))
|
|
41
|
+
if err := r.ParseMultipartForm(1 << 20); err != nil {
|
|
42
|
+
t.Error(err)
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
defer r.MultipartForm.RemoveAll()
|
|
46
|
+
files := r.MultipartForm.File["file"]
|
|
47
|
+
if len(files) != 1 || files[0].Filename != name {
|
|
48
|
+
t.Errorf("unexpected file parts: %#v", files)
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
contentType := files[0].Header.Get("Content-Type")
|
|
52
|
+
prefix := "audio/"
|
|
53
|
+
if strings.HasPrefix(name, "image") {
|
|
54
|
+
prefix = "image/"
|
|
55
|
+
} else if strings.HasPrefix(name, "video") {
|
|
56
|
+
prefix = "video/"
|
|
57
|
+
}
|
|
58
|
+
if !strings.HasPrefix(contentType, prefix) {
|
|
59
|
+
t.Errorf("content type = %s, want %s", contentType, prefix)
|
|
60
|
+
}
|
|
61
|
+
file, err := files[0].Open()
|
|
62
|
+
if err != nil {
|
|
63
|
+
t.Error(err)
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
defer file.Close()
|
|
67
|
+
data, err := io.ReadAll(file)
|
|
68
|
+
if err != nil || string(data) != "media-content" {
|
|
69
|
+
t.Errorf("uploaded content = %q, err = %v", data, err)
|
|
70
|
+
}
|
|
71
|
+
_, _ = w.Write([]byte(`{"ret":"0","data":{"pippit_asset_id":"asset_original"}}`))
|
|
72
|
+
}))
|
|
73
|
+
defer server.Close()
|
|
74
|
+
var stdout, stderr bytes.Buffer
|
|
75
|
+
root := newTestRootCommand(t, &stdout, &stderr, server.URL)
|
|
76
|
+
root.SetArgs([]string{"upload-file", "--path", path})
|
|
77
|
+
if err := root.Execute(); err != nil {
|
|
78
|
+
t.Fatal(err)
|
|
79
|
+
}
|
|
80
|
+
if stdout.String() != "{\"asset_id\":\"asset_original\"}\n" || calls != 1 {
|
|
81
|
+
t.Fatalf("stdout = %q, requests = %d", stdout.String(), calls)
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
func TestValidateMediaUpload(t *testing.T) {
|
|
88
|
+
dir := t.TempDir()
|
|
89
|
+
for _, tc := range []struct {
|
|
90
|
+
name string
|
|
91
|
+
size int64
|
|
92
|
+
ok bool
|
|
93
|
+
}{
|
|
94
|
+
{"below-limit.png", maxMediaUploadBytes - 1, true},
|
|
95
|
+
{"at-limit.png", maxMediaUploadBytes, false},
|
|
96
|
+
{"over-limit.png", maxMediaUploadBytes + 1, false},
|
|
97
|
+
{"script.docx", 1, false},
|
|
98
|
+
{"audio.flac", 1, false},
|
|
99
|
+
{"file.unknown", 1, false},
|
|
100
|
+
} {
|
|
101
|
+
path := filepath.Join(dir, tc.name)
|
|
102
|
+
if err := os.WriteFile(path, nil, 0o600); err != nil {
|
|
103
|
+
t.Fatal(err)
|
|
104
|
+
}
|
|
105
|
+
// Sparse files exercise the exact size boundary without large allocations.
|
|
106
|
+
if err := os.Truncate(path, tc.size); err != nil {
|
|
107
|
+
t.Fatal(err)
|
|
108
|
+
}
|
|
109
|
+
if err := validateMediaUpload(path); (err == nil) != tc.ok {
|
|
110
|
+
t.Errorf("validateMediaUpload(%s) = %v, want accepted=%t", tc.name, err, tc.ok)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
for _, path := range []string{"", dir, filepath.Join(dir, "missing.png")} {
|
|
114
|
+
if err := validateMediaUpload(path); err == nil {
|
|
115
|
+
t.Errorf("validateMediaUpload(%q) should fail", path)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
func TestUploadFileCommandHelpAndArguments(t *testing.T) {
|
|
121
|
+
var stdout, stderr bytes.Buffer
|
|
122
|
+
root := newRootCommand(&stdout, &stderr, nil)
|
|
123
|
+
root.SetArgs([]string{"upload-file", "--help"})
|
|
124
|
+
if err := root.Execute(); err != nil || !strings.Contains(stdout.String(), "--path") {
|
|
125
|
+
t.Fatalf("help must work without credentials: %v, %s", err, stdout.String())
|
|
126
|
+
}
|
|
127
|
+
for _, args := range [][]string{{"upload-file"}, {"upload-file", "--path", " "}, {"upload-file", "unexpected"}} {
|
|
128
|
+
root := newRootCommand(&stdout, &stderr, nil)
|
|
129
|
+
root.SetArgs(args)
|
|
130
|
+
if err := root.Execute(); err == nil {
|
|
131
|
+
t.Errorf("Execute(%v) should fail", args)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
func TestUploadFileRejectsInvalidResponse(t *testing.T) {
|
|
137
|
+
path := filepath.Join(t.TempDir(), "image.png")
|
|
138
|
+
if err := os.WriteFile(path, []byte("media"), 0o600); err != nil {
|
|
139
|
+
t.Fatal(err)
|
|
140
|
+
}
|
|
141
|
+
for _, response := range []string{`{"ret":"1","errmsg":"rejected"}`, `{"ret":"0","data":{"asset_id":"wrong_field"}}`} {
|
|
142
|
+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
143
|
+
_, _ = io.Copy(io.Discard, r.Body)
|
|
144
|
+
_, _ = w.Write([]byte(response))
|
|
145
|
+
}))
|
|
146
|
+
var stdout, stderr bytes.Buffer
|
|
147
|
+
root := newTestRootCommand(t, &stdout, &stderr, server.URL)
|
|
148
|
+
root.SetArgs([]string{"upload-file", "--path", path})
|
|
149
|
+
err := root.Execute()
|
|
150
|
+
server.Close()
|
|
151
|
+
if err == nil || stdout.Len() != 0 {
|
|
152
|
+
t.Errorf("response %s: error = %v, stdout = %s", response, err, stdout.String())
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
package/dist/checksums.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
259ad5fb69598b646e035e5dd70fd6c905a25128b1b9c8e9689c58e09ec0a3d9 pippit-tool-cli-1.0.23-darwin-amd64.tar.gz
|
|
2
|
+
610ec13e42a53a472759daf322701e178b062ff2ca2e854382cf5a8b6b894953 pippit-tool-cli-1.0.23-darwin-arm64.tar.gz
|
|
3
|
+
3d371bb915e4b6adaf29cd3b1b06a9ee6a40896f962051ce97adeb1937a1b4a1 pippit-tool-cli-1.0.23-linux-amd64.tar.gz
|
|
4
|
+
541f8a8e1913a591ad98ed3835525eba8459fd12ec289f9b206f9588812311c8 pippit-tool-cli-1.0.23-linux-arm64.tar.gz
|
|
5
|
+
0ded272a827aa39427c53135465c99e6e1f94c81ef1f5e0982c6726d385e03bf pippit-tool-cli-1.0.23-windows-amd64.zip
|
|
6
|
+
ab6fd1baeac84e4e3cdde2392462534bd2a6279ee1cab0395437f1a03b5ace55 pippit-tool-cli-1.0.23-windows-arm64.zip
|