@pippit-dev/cli 0.0.2 → 0.0.4
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.
|
@@ -21,9 +21,11 @@ func (a *accessKeyAuthorizer) Inject(ctx context.Context, req *http.Request) err
|
|
|
21
21
|
if err := ctx.Err(); err != nil {
|
|
22
22
|
return err
|
|
23
23
|
}
|
|
24
|
-
if a.accessKey == "" {
|
|
24
|
+
if a.accessKey == "" && req.Method == http.MethodPost {
|
|
25
25
|
return fmt.Errorf("%s is required for authenticated requests", config.EnvXYQAccessKey)
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
if a.accessKey != "" {
|
|
28
|
+
req.Header.Set("Authorization", "Bearer "+a.accessKey)
|
|
29
|
+
}
|
|
28
30
|
return nil
|
|
29
31
|
}
|
|
@@ -7,6 +7,7 @@ import (
|
|
|
7
7
|
"io"
|
|
8
8
|
"net/http"
|
|
9
9
|
"net/url"
|
|
10
|
+
"reflect"
|
|
10
11
|
"strings"
|
|
11
12
|
"time"
|
|
12
13
|
|
|
@@ -40,50 +41,77 @@ func NewHTTPClient(baseURL string, timeout time.Duration, authorizer RequestAuth
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
func (c *httpClient) SendRequest(ctx context.Context, path string, body any, out any) error {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
req, err := c.newJSONRequest(ctx, path, body)
|
|
47
|
-
if err != nil {
|
|
48
|
-
return err
|
|
49
|
-
}
|
|
50
|
-
if err := c.authorizer.Inject(ctx, req); err != nil {
|
|
51
|
-
return fmt.Errorf("inject auth headers: %w", err)
|
|
44
|
+
method := http.MethodPost
|
|
45
|
+
if body == nil {
|
|
46
|
+
method = http.MethodGet
|
|
52
47
|
}
|
|
53
|
-
return c.do(req, out)
|
|
54
|
-
}
|
|
55
48
|
|
|
56
|
-
func (c *httpClient) newJSONRequest(ctx context.Context, path string, body any) (*http.Request, error) {
|
|
57
49
|
reqURL, err := c.resolveURL(path, nil)
|
|
58
50
|
if err != nil {
|
|
59
|
-
return
|
|
51
|
+
return err
|
|
60
52
|
}
|
|
53
|
+
|
|
61
54
|
var reader io.Reader
|
|
62
55
|
if body != nil {
|
|
63
56
|
payload, err := sonic.Marshal(body)
|
|
64
57
|
if err != nil {
|
|
65
|
-
return
|
|
58
|
+
return fmt.Errorf("encode body: %w", err)
|
|
66
59
|
}
|
|
67
60
|
reader = bytes.NewReader(payload)
|
|
68
61
|
}
|
|
69
|
-
|
|
62
|
+
|
|
63
|
+
req, err := http.NewRequestWithContext(ctx, method, reqURL, reader)
|
|
70
64
|
if err != nil {
|
|
71
|
-
return
|
|
65
|
+
return fmt.Errorf("build %s request: %w", method, err)
|
|
72
66
|
}
|
|
73
67
|
if body != nil {
|
|
74
68
|
req.Header.Set("Content-Type", "application/json")
|
|
75
69
|
}
|
|
76
|
-
|
|
70
|
+
|
|
71
|
+
if c.authorizer == nil {
|
|
72
|
+
return fmt.Errorf("authorized request requires authorizer")
|
|
73
|
+
}
|
|
74
|
+
if err := c.authorizer.Inject(ctx, req); err != nil {
|
|
75
|
+
return fmt.Errorf("inject auth headers: %w", err)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
c.injectHeaders(req)
|
|
79
|
+
|
|
80
|
+
// If out is **http.Response, return the raw response for streaming (e.g. file download).
|
|
81
|
+
if out != nil {
|
|
82
|
+
if rv := reflect.ValueOf(out); rv.Kind() == reflect.Ptr && rv.Elem().Kind() == reflect.Ptr {
|
|
83
|
+
if rv.Elem().Type().Elem() == reflect.TypeOf(http.Response{}) {
|
|
84
|
+
resp, err := c.httpClient.Do(req)
|
|
85
|
+
if err != nil {
|
|
86
|
+
return fmt.Errorf("%s %s failed: %w", method, reqURL, err)
|
|
87
|
+
}
|
|
88
|
+
if resp.StatusCode >= 400 {
|
|
89
|
+
defer resp.Body.Close()
|
|
90
|
+
return fmt.Errorf("%s %s returned HTTP %d", method, reqURL, resp.StatusCode)
|
|
91
|
+
}
|
|
92
|
+
rv.Elem().Set(reflect.ValueOf(resp))
|
|
93
|
+
return nil
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
req.Header.Set("Accept", "application/json")
|
|
99
|
+
|
|
100
|
+
return c.do(req, out)
|
|
77
101
|
}
|
|
78
102
|
|
|
79
|
-
func (c *httpClient)
|
|
103
|
+
func (c *httpClient) injectHeaders(req *http.Request) {
|
|
80
104
|
for k, values := range c.headers {
|
|
81
105
|
for _, v := range values {
|
|
82
106
|
req.Header.Add(k, v)
|
|
83
107
|
}
|
|
84
108
|
}
|
|
85
|
-
req.Header.Set("
|
|
109
|
+
req.Header.Set("User-Agent", "Pippit-CLI/1.0")
|
|
110
|
+
req.Header.Set("x-use-ppe", "1")
|
|
111
|
+
req.Header.Set("x-tt-env", "ppe_harness_novel_v2")
|
|
112
|
+
}
|
|
86
113
|
|
|
114
|
+
func (c *httpClient) do(req *http.Request, out any) error {
|
|
87
115
|
resp, err := c.httpClient.Do(req)
|
|
88
116
|
if err != nil {
|
|
89
117
|
return fmt.Errorf("%s %s failed: %w", req.Method, req.URL.String(), err)
|
|
@@ -15,6 +15,10 @@ import (
|
|
|
15
15
|
"time"
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
+
// DownloadClient is a minimal interface for downloading files via HTTP GET.
|
|
19
|
+
// It is satisfied by common.Client so that download logic can use the same
|
|
20
|
+
// HTTP infrastructure (headers, auth, timeouts) as API calls.
|
|
21
|
+
|
|
18
22
|
// DownloadResultOptions is the command-facing shape for downloading one result URL.
|
|
19
23
|
type DownloadResultOptions struct {
|
|
20
24
|
URL string `json:"url"`
|
|
@@ -51,7 +55,7 @@ type downloadTaskResult struct {
|
|
|
51
55
|
err error
|
|
52
56
|
}
|
|
53
57
|
|
|
54
|
-
func DownloadResult(ctx context.Context, opts DownloadResultOptions,
|
|
58
|
+
func DownloadResult(ctx context.Context, opts DownloadResultOptions, runner *Runner) (*DownloadResultResponse, error) {
|
|
55
59
|
if err := ctx.Err(); err != nil {
|
|
56
60
|
return nil, err
|
|
57
61
|
}
|
|
@@ -104,7 +108,6 @@ func DownloadResult(ctx context.Context, opts DownloadResultOptions, _ *Runner)
|
|
|
104
108
|
workers = len(tasks)
|
|
105
109
|
}
|
|
106
110
|
|
|
107
|
-
client := &http.Client{Timeout: 600 * time.Second}
|
|
108
111
|
taskCh := make(chan downloadTask)
|
|
109
112
|
resultCh := make(chan downloadTaskResult, len(tasks))
|
|
110
113
|
|
|
@@ -116,7 +119,7 @@ func DownloadResult(ctx context.Context, opts DownloadResultOptions, _ *Runner)
|
|
|
116
119
|
for task := range taskCh {
|
|
117
120
|
resultCh <- downloadTaskResult{
|
|
118
121
|
filepath: task.filepath,
|
|
119
|
-
err: downloadFileWithRetry(ctx,
|
|
122
|
+
err: downloadFileWithRetry(ctx, runner.Client, task.url, task.filepath),
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
125
|
}()
|
|
@@ -167,7 +170,7 @@ func DownloadResult(ctx context.Context, opts DownloadResultOptions, _ *Runner)
|
|
|
167
170
|
return result, nil
|
|
168
171
|
}
|
|
169
172
|
|
|
170
|
-
func downloadFileWithRetry(ctx context.Context, client
|
|
173
|
+
func downloadFileWithRetry(ctx context.Context, client Client, rawURL string, targetPath string) error {
|
|
171
174
|
var lastErr error
|
|
172
175
|
for attempt := 0; attempt <= maxDownloadRetries; attempt++ {
|
|
173
176
|
if attempt > 0 {
|
|
@@ -203,23 +206,13 @@ func isRetryableError(err error) bool {
|
|
|
203
206
|
return false
|
|
204
207
|
}
|
|
205
208
|
|
|
206
|
-
func downloadFile(ctx context.Context, client
|
|
207
|
-
|
|
208
|
-
if err != nil {
|
|
209
|
-
return fmt.Errorf("build download request: %w", err)
|
|
210
|
-
}
|
|
211
|
-
req.Header.Set("User-Agent", "Pippit-CLI/1.0")
|
|
212
|
-
|
|
213
|
-
resp, err := client.Do(req)
|
|
214
|
-
if err != nil {
|
|
209
|
+
func downloadFile(ctx context.Context, client Client, rawURL string, targetPath string) error {
|
|
210
|
+
var resp *http.Response
|
|
211
|
+
if err := client.SendRequest(ctx, rawURL, nil, &resp); err != nil {
|
|
215
212
|
return err
|
|
216
213
|
}
|
|
217
214
|
defer resp.Body.Close()
|
|
218
215
|
|
|
219
|
-
if resp.StatusCode >= 400 {
|
|
220
|
-
return fmt.Errorf("HTTP %d: %s", resp.StatusCode, http.StatusText(resp.StatusCode))
|
|
221
|
-
}
|
|
222
|
-
|
|
223
216
|
tmpPath := targetPath + ".tmp"
|
|
224
217
|
out, err := os.Create(tmpPath)
|
|
225
218
|
if err != nil {
|
package/package.json
CHANGED
|
@@ -39,11 +39,14 @@ function whichPippitCli() {
|
|
|
39
39
|
function main() {
|
|
40
40
|
const installed = getGloballyInstalledVersion();
|
|
41
41
|
if (installed) {
|
|
42
|
-
console.log(`pippit-cli
|
|
42
|
+
console.log(`Updating global pippit-cli (${installed}) via ${PKG}...`);
|
|
43
43
|
} else {
|
|
44
44
|
console.log(`Installing ${PKG} globally...`);
|
|
45
|
-
run("npm", ["install", "-g", PKG], { timeout: 120000 });
|
|
46
45
|
}
|
|
46
|
+
run("npm", ["install", "-g", PKG], {
|
|
47
|
+
timeout: 120000,
|
|
48
|
+
env: { ...process.env, PIPPIT_CLI_SKIP_SKILLS: "1" },
|
|
49
|
+
});
|
|
47
50
|
|
|
48
51
|
console.log("Installing pippit-cli skills...");
|
|
49
52
|
try {
|