@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
- req.Header.Set("Authorization", "Bearer "+a.accessKey)
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
- if c.authorizer == nil {
44
- return fmt.Errorf("authorized request requires authorizer")
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 nil, err
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 nil, fmt.Errorf("encode POST body: %w", err)
58
+ return fmt.Errorf("encode body: %w", err)
66
59
  }
67
60
  reader = bytes.NewReader(payload)
68
61
  }
69
- req, err := http.NewRequestWithContext(ctx, http.MethodPost, reqURL, reader)
62
+
63
+ req, err := http.NewRequestWithContext(ctx, method, reqURL, reader)
70
64
  if err != nil {
71
- return nil, fmt.Errorf("build POST request: %w", err)
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
- return req, nil
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) do(req *http.Request, out any) error {
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("Accept", "application/json")
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, _ *Runner) (*DownloadResultResponse, error) {
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, client, task.url, task.filepath),
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 *http.Client, rawURL string, targetPath string) error {
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 *http.Client, rawURL string, targetPath string) error {
207
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, rawURL, nil)
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pippit-dev/cli",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Pippit CLI",
5
5
  "bin": {
6
6
  "pippit-cli": "scripts/run.js"
@@ -39,11 +39,14 @@ function whichPippitCli() {
39
39
  function main() {
40
40
  const installed = getGloballyInstalledVersion();
41
41
  if (installed) {
42
- console.log(`pippit-cli is already installed globally (${installed}).`);
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 {