@socketsecurity/lib 5.5.3 → 5.7.0

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.
@@ -435,46 +435,6 @@ export interface HttpDownloadResult {
435
435
  */
436
436
  size: number;
437
437
  }
438
- /**
439
- * Make an HTTP/HTTPS request with retry logic and redirect support.
440
- * Provides a fetch-like API using Node.js native http/https modules.
441
- *
442
- * This is the main entry point for making HTTP requests. It handles retries,
443
- * redirects, timeouts, and provides a fetch-compatible response interface.
444
- *
445
- * @param url - The URL to request (must start with http:// or https://)
446
- * @param options - Request configuration options
447
- * @returns Promise resolving to response object with `.json()`, `.text()`, etc.
448
- * @throws {Error} When all retries are exhausted, timeout occurs, or non-retryable error happens
449
- *
450
- * @example
451
- * ```ts
452
- * // Simple GET request
453
- * const response = await httpRequest('https://api.example.com/data')
454
- * const data = response.json()
455
- *
456
- * // POST with JSON body
457
- * const response = await httpRequest('https://api.example.com/users', {
458
- * method: 'POST',
459
- * headers: { 'Content-Type': 'application/json' },
460
- * body: JSON.stringify({ name: 'Alice', email: 'alice@example.com' })
461
- * })
462
- *
463
- * // With retries and timeout
464
- * const response = await httpRequest('https://api.example.com/data', {
465
- * retries: 3,
466
- * retryDelay: 1000,
467
- * timeout: 60000
468
- * })
469
- *
470
- * // Don't follow redirects
471
- * const response = await httpRequest('https://example.com/redirect', {
472
- * followRedirects: false
473
- * })
474
- * console.log(response.status) // 301, 302, etc.
475
- * ```
476
- */
477
- export declare function httpRequest(url: string, options?: HttpRequestOptions | undefined): Promise<HttpResponse>;
478
438
  /**
479
439
  * Download a file from a URL to a local path with redirect support, retry logic, and progress callbacks.
480
440
  * Uses streaming to avoid loading entire file in memory.
@@ -533,8 +493,12 @@ export declare function httpRequest(url: string, options?: HttpRequestOptions |
533
493
  */
534
494
  export declare function httpDownload(url: string, destPath: string, options?: HttpDownloadOptions | undefined): Promise<HttpDownloadResult>;
535
495
  /**
536
- * Perform a GET request and parse JSON response.
537
- * Convenience wrapper around `httpRequest` for common JSON API calls.
496
+ * Perform an HTTP request and parse JSON response.
497
+ * Convenience wrapper around `httpRequest` for JSON API calls.
498
+ * Automatically sets appropriate headers for JSON requests:
499
+ * - `Accept: application/json` (always)
500
+ * - `Content-Type: application/json` (when body is present)
501
+ * User-provided headers override these defaults.
538
502
  *
539
503
  * @template T - Expected JSON response type (defaults to `unknown`)
540
504
  * @param url - The URL to request (must start with http:// or https://)
@@ -544,34 +508,79 @@ export declare function httpDownload(url: string, destPath: string, options?: Ht
544
508
  *
545
509
  * @example
546
510
  * ```ts
547
- * // Simple JSON GET
548
- * const data = await httpGetJson('https://api.example.com/data')
511
+ * // Simple JSON GET (automatically sets Accept: application/json)
512
+ * const data = await httpJson('https://api.example.com/data')
549
513
  * console.log(data)
550
514
  *
551
515
  * // With type safety
552
516
  * interface User { id: number; name: string; email: string }
553
- * const user = await httpGetJson<User>('https://api.example.com/user/123')
517
+ * const user = await httpJson<User>('https://api.example.com/user/123')
554
518
  * console.log(user.name, user.email)
555
519
  *
556
- * // With custom headers
557
- * const data = await httpGetJson('https://api.example.com/data', {
558
- * headers: {
559
- * 'Authorization': 'Bearer token123',
560
- * 'Accept': 'application/json'
561
- * }
520
+ * // POST with JSON body (automatically sets Content-Type: application/json)
521
+ * const result = await httpJson('https://api.example.com/users', {
522
+ * method: 'POST',
523
+ * body: JSON.stringify({ name: 'Alice', email: 'alice@example.com' })
562
524
  * })
563
525
  *
564
- * // With retries
565
- * const data = await httpGetJson('https://api.example.com/data', {
526
+ * // With custom headers and retries
527
+ * const data = await httpJson('https://api.example.com/data', {
528
+ * headers: {
529
+ * 'Authorization': 'Bearer token123'
530
+ * },
566
531
  * retries: 3,
567
532
  * retryDelay: 1000
568
533
  * })
569
534
  * ```
570
535
  */
571
- export declare function httpGetJson<T = unknown>(url: string, options?: HttpRequestOptions | undefined): Promise<T>;
536
+ export declare function httpJson<T = unknown>(url: string, options?: HttpRequestOptions | undefined): Promise<T>;
537
+ /**
538
+ * Make an HTTP/HTTPS request with retry logic and redirect support.
539
+ * Provides a fetch-like API using Node.js native http/https modules.
540
+ *
541
+ * This is the main entry point for making HTTP requests. It handles retries,
542
+ * redirects, timeouts, and provides a fetch-compatible response interface.
543
+ *
544
+ * @param url - The URL to request (must start with http:// or https://)
545
+ * @param options - Request configuration options
546
+ * @returns Promise resolving to response object with `.json()`, `.text()`, etc.
547
+ * @throws {Error} When all retries are exhausted, timeout occurs, or non-retryable error happens
548
+ *
549
+ * @example
550
+ * ```ts
551
+ * // Simple GET request
552
+ * const response = await httpRequest('https://api.example.com/data')
553
+ * const data = response.json()
554
+ *
555
+ * // POST with JSON body
556
+ * const response = await httpRequest('https://api.example.com/users', {
557
+ * method: 'POST',
558
+ * headers: { 'Content-Type': 'application/json' },
559
+ * body: JSON.stringify({ name: 'Alice', email: 'alice@example.com' })
560
+ * })
561
+ *
562
+ * // With retries and timeout
563
+ * const response = await httpRequest('https://api.example.com/data', {
564
+ * retries: 3,
565
+ * retryDelay: 1000,
566
+ * timeout: 60000
567
+ * })
568
+ *
569
+ * // Don't follow redirects
570
+ * const response = await httpRequest('https://example.com/redirect', {
571
+ * followRedirects: false
572
+ * })
573
+ * console.log(response.status) // 301, 302, etc.
574
+ * ```
575
+ */
576
+ export declare function httpRequest(url: string, options?: HttpRequestOptions | undefined): Promise<HttpResponse>;
572
577
  /**
573
- * Perform a GET request and return text response.
578
+ * Perform an HTTP request and return text response.
574
579
  * Convenience wrapper around `httpRequest` for fetching text content.
580
+ * Automatically sets appropriate headers for text requests:
581
+ * - `Accept: text/plain` (always)
582
+ * - `Content-Type: text/plain` (when body is present)
583
+ * User-provided headers override these defaults.
575
584
  *
576
585
  * @param url - The URL to request (must start with http:// or https://)
577
586
  * @param options - Request configuration options
@@ -580,25 +589,32 @@ export declare function httpGetJson<T = unknown>(url: string, options?: HttpRequ
580
589
  *
581
590
  * @example
582
591
  * ```ts
583
- * // Fetch HTML
584
- * const html = await httpGetText('https://example.com')
592
+ * // Fetch HTML (automatically sets Accept: text/plain)
593
+ * const html = await httpText('https://example.com')
585
594
  * console.log(html.includes('<!DOCTYPE html>'))
586
595
  *
587
596
  * // Fetch plain text
588
- * const text = await httpGetText('https://example.com/file.txt')
597
+ * const text = await httpText('https://example.com/file.txt')
589
598
  * console.log(text)
590
599
  *
591
- * // With custom headers
592
- * const text = await httpGetText('https://example.com/data.txt', {
600
+ * // POST with text body (automatically sets Content-Type: text/plain)
601
+ * const result = await httpText('https://example.com/api', {
602
+ * method: 'POST',
603
+ * body: 'raw text data'
604
+ * })
605
+ *
606
+ * // With custom headers (override defaults)
607
+ * const text = await httpText('https://example.com/data.txt', {
593
608
  * headers: {
594
- * 'Authorization': 'Bearer token123'
609
+ * 'Authorization': 'Bearer token123',
610
+ * 'Accept': 'text/html' // Override default Accept header
595
611
  * }
596
612
  * })
597
613
  *
598
614
  * // With timeout
599
- * const text = await httpGetText('https://example.com/large-file.txt', {
615
+ * const text = await httpText('https://example.com/large-file.txt', {
600
616
  * timeout: 60000 // 1 minute
601
617
  * })
602
618
  * ```
603
619
  */
604
- export declare function httpGetText(url: string, options?: HttpRequestOptions | undefined): Promise<string>;
620
+ export declare function httpText(url: string, options?: HttpRequestOptions | undefined): Promise<string>;
@@ -20,9 +20,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  var http_request_exports = {};
21
21
  __export(http_request_exports, {
22
22
  httpDownload: () => httpDownload,
23
- httpGetJson: () => httpGetJson,
24
- httpGetText: () => httpGetText,
25
- httpRequest: () => httpRequest
23
+ httpJson: () => httpJson,
24
+ httpRequest: () => httpRequest,
25
+ httpText: () => httpText
26
26
  });
27
27
  module.exports = __toCommonJS(http_request_exports);
28
28
  let _fs;
@@ -49,38 +49,131 @@ function getHttps() {
49
49
  }
50
50
  return _https;
51
51
  }
52
- async function httpRequest(url, options) {
52
+ async function httpDownloadAttempt(url, destPath, options) {
53
53
  const {
54
- body,
55
54
  followRedirects = true,
56
55
  headers = {},
57
56
  maxRedirects = 5,
58
- method = "GET",
59
- retries = 0,
60
- retryDelay = 1e3,
61
- timeout = 3e4
57
+ onProgress,
58
+ timeout = 12e4
62
59
  } = { __proto__: null, ...options };
63
- let lastError;
64
- for (let attempt = 0; attempt <= retries; attempt++) {
65
- try {
66
- return await httpRequestAttempt(url, {
67
- body,
68
- followRedirects,
69
- headers,
70
- maxRedirects,
71
- method,
72
- timeout
73
- });
74
- } catch (e) {
75
- lastError = e;
76
- if (attempt === retries) {
77
- break;
60
+ return await new Promise((resolve, reject) => {
61
+ const parsedUrl = new URL(url);
62
+ const isHttps = parsedUrl.protocol === "https:";
63
+ const httpModule = isHttps ? /* @__PURE__ */ getHttps() : /* @__PURE__ */ getHttp();
64
+ const requestOptions = {
65
+ headers: {
66
+ "User-Agent": "socket-registry/1.0",
67
+ ...headers
68
+ },
69
+ hostname: parsedUrl.hostname,
70
+ method: "GET",
71
+ path: parsedUrl.pathname + parsedUrl.search,
72
+ port: parsedUrl.port,
73
+ timeout
74
+ };
75
+ const { createWriteStream } = /* @__PURE__ */ getFs();
76
+ let fileStream;
77
+ let streamClosed = false;
78
+ const closeStream = () => {
79
+ if (!streamClosed && fileStream) {
80
+ streamClosed = true;
81
+ fileStream.close();
78
82
  }
79
- const delayMs = retryDelay * 2 ** attempt;
80
- await new Promise((resolve) => setTimeout(resolve, delayMs));
81
- }
82
- }
83
- throw lastError || new Error("Request failed after retries");
83
+ };
84
+ const request = httpModule.request(
85
+ requestOptions,
86
+ (res) => {
87
+ if (followRedirects && res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
88
+ if (maxRedirects <= 0) {
89
+ reject(
90
+ new Error(
91
+ `Too many redirects (exceeded maximum: ${maxRedirects})`
92
+ )
93
+ );
94
+ return;
95
+ }
96
+ const redirectUrl = res.headers.location.startsWith("http") ? res.headers.location : new URL(res.headers.location, url).toString();
97
+ resolve(
98
+ httpDownloadAttempt(redirectUrl, destPath, {
99
+ followRedirects,
100
+ headers,
101
+ maxRedirects: maxRedirects - 1,
102
+ onProgress,
103
+ timeout
104
+ })
105
+ );
106
+ return;
107
+ }
108
+ if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) {
109
+ closeStream();
110
+ reject(
111
+ new Error(
112
+ `Download failed: HTTP ${res.statusCode} ${res.statusMessage}`
113
+ )
114
+ );
115
+ return;
116
+ }
117
+ const totalSize = Number.parseInt(
118
+ res.headers["content-length"] || "0",
119
+ 10
120
+ );
121
+ let downloadedSize = 0;
122
+ fileStream = createWriteStream(destPath);
123
+ fileStream.on("error", (error) => {
124
+ closeStream();
125
+ const err = new Error(`Failed to write file: ${error.message}`, {
126
+ cause: error
127
+ });
128
+ reject(err);
129
+ });
130
+ res.on("data", (chunk) => {
131
+ downloadedSize += chunk.length;
132
+ if (onProgress && totalSize > 0) {
133
+ onProgress(downloadedSize, totalSize);
134
+ }
135
+ });
136
+ res.on("end", () => {
137
+ fileStream?.close(() => {
138
+ streamClosed = true;
139
+ resolve({
140
+ path: destPath,
141
+ size: downloadedSize
142
+ });
143
+ });
144
+ });
145
+ res.on("error", (error) => {
146
+ closeStream();
147
+ reject(error);
148
+ });
149
+ res.pipe(fileStream);
150
+ }
151
+ );
152
+ request.on("error", (error) => {
153
+ closeStream();
154
+ const code = error.code;
155
+ let message = `HTTP download failed for ${url}: ${error.message}
156
+ `;
157
+ if (code === "ENOTFOUND") {
158
+ message += "DNS lookup failed. Check the hostname and your network connection.";
159
+ } else if (code === "ECONNREFUSED") {
160
+ message += "Connection refused. Verify the server is running and accessible.";
161
+ } else if (code === "ETIMEDOUT") {
162
+ message += "Request timed out. Check your network or increase the timeout value.";
163
+ } else if (code === "ECONNRESET") {
164
+ message += "Connection reset. The server may have closed the connection unexpectedly.";
165
+ } else {
166
+ message += "Check your network connection and verify the URL is correct.";
167
+ }
168
+ reject(new Error(message, { cause: error }));
169
+ });
170
+ request.on("timeout", () => {
171
+ request.destroy();
172
+ closeStream();
173
+ reject(new Error(`Download timed out after ${timeout}ms`));
174
+ });
175
+ request.end();
176
+ });
84
177
  }
85
178
  async function httpRequestAttempt(url, options) {
86
179
  const {
@@ -236,134 +329,30 @@ async function httpDownload(url, destPath, options) {
236
329
  }
237
330
  throw lastError || new Error("Download failed after retries");
238
331
  }
239
- async function httpDownloadAttempt(url, destPath, options) {
332
+ async function httpJson(url, options) {
240
333
  const {
241
- followRedirects = true,
334
+ body,
242
335
  headers = {},
243
- maxRedirects = 5,
244
- onProgress,
245
- timeout = 12e4
246
- } = { __proto__: null, ...options };
247
- return await new Promise((resolve, reject) => {
248
- const parsedUrl = new URL(url);
249
- const isHttps = parsedUrl.protocol === "https:";
250
- const httpModule = isHttps ? /* @__PURE__ */ getHttps() : /* @__PURE__ */ getHttp();
251
- const requestOptions = {
252
- headers: {
253
- "User-Agent": "socket-registry/1.0",
254
- ...headers
255
- },
256
- hostname: parsedUrl.hostname,
257
- method: "GET",
258
- path: parsedUrl.pathname + parsedUrl.search,
259
- port: parsedUrl.port,
260
- timeout
261
- };
262
- const { createWriteStream } = /* @__PURE__ */ getFs();
263
- let fileStream;
264
- let streamClosed = false;
265
- const closeStream = () => {
266
- if (!streamClosed && fileStream) {
267
- streamClosed = true;
268
- fileStream.close();
269
- }
270
- };
271
- const request = httpModule.request(
272
- requestOptions,
273
- (res) => {
274
- if (followRedirects && res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
275
- if (maxRedirects <= 0) {
276
- reject(
277
- new Error(
278
- `Too many redirects (exceeded maximum: ${maxRedirects})`
279
- )
280
- );
281
- return;
282
- }
283
- const redirectUrl = res.headers.location.startsWith("http") ? res.headers.location : new URL(res.headers.location, url).toString();
284
- resolve(
285
- httpDownloadAttempt(redirectUrl, destPath, {
286
- followRedirects,
287
- headers,
288
- maxRedirects: maxRedirects - 1,
289
- onProgress,
290
- timeout
291
- })
292
- );
293
- return;
294
- }
295
- if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) {
296
- closeStream();
297
- reject(
298
- new Error(
299
- `Download failed: HTTP ${res.statusCode} ${res.statusMessage}`
300
- )
301
- );
302
- return;
303
- }
304
- const totalSize = Number.parseInt(
305
- res.headers["content-length"] || "0",
306
- 10
307
- );
308
- let downloadedSize = 0;
309
- fileStream = createWriteStream(destPath);
310
- fileStream.on("error", (error) => {
311
- closeStream();
312
- const err = new Error(`Failed to write file: ${error.message}`, {
313
- cause: error
314
- });
315
- reject(err);
316
- });
317
- res.on("data", (chunk) => {
318
- downloadedSize += chunk.length;
319
- if (onProgress && totalSize > 0) {
320
- onProgress(downloadedSize, totalSize);
321
- }
322
- });
323
- res.on("end", () => {
324
- fileStream?.close(() => {
325
- streamClosed = true;
326
- resolve({
327
- path: destPath,
328
- size: downloadedSize
329
- });
330
- });
331
- });
332
- res.on("error", (error) => {
333
- closeStream();
334
- reject(error);
335
- });
336
- res.pipe(fileStream);
337
- }
338
- );
339
- request.on("error", (error) => {
340
- closeStream();
341
- const code = error.code;
342
- let message = `HTTP download failed for ${url}: ${error.message}
343
- `;
344
- if (code === "ENOTFOUND") {
345
- message += "DNS lookup failed. Check the hostname and your network connection.";
346
- } else if (code === "ECONNREFUSED") {
347
- message += "Connection refused. Verify the server is running and accessible.";
348
- } else if (code === "ETIMEDOUT") {
349
- message += "Request timed out. Check your network or increase the timeout value.";
350
- } else if (code === "ECONNRESET") {
351
- message += "Connection reset. The server may have closed the connection unexpectedly.";
352
- } else {
353
- message += "Check your network connection and verify the URL is correct.";
354
- }
355
- reject(new Error(message, { cause: error }));
356
- });
357
- request.on("timeout", () => {
358
- request.destroy();
359
- closeStream();
360
- reject(new Error(`Download timed out after ${timeout}ms`));
361
- });
362
- request.end();
336
+ ...restOptions
337
+ } = {
338
+ __proto__: null,
339
+ ...options
340
+ };
341
+ const defaultHeaders = {
342
+ Accept: "application/json"
343
+ };
344
+ if (body) {
345
+ defaultHeaders["Content-Type"] = "application/json";
346
+ }
347
+ const mergedHeaders = {
348
+ ...defaultHeaders,
349
+ ...headers
350
+ };
351
+ const response = await httpRequest(url, {
352
+ body,
353
+ headers: mergedHeaders,
354
+ ...restOptions
363
355
  });
364
- }
365
- async function httpGetJson(url, options) {
366
- const response = await httpRequest(url, { ...options, method: "GET" });
367
356
  if (!response.ok) {
368
357
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
369
358
  }
@@ -373,8 +362,63 @@ async function httpGetJson(url, options) {
373
362
  throw new Error("Failed to parse JSON response", { cause: e });
374
363
  }
375
364
  }
376
- async function httpGetText(url, options) {
377
- const response = await httpRequest(url, { ...options, method: "GET" });
365
+ async function httpRequest(url, options) {
366
+ const {
367
+ body,
368
+ followRedirects = true,
369
+ headers = {},
370
+ maxRedirects = 5,
371
+ method = "GET",
372
+ retries = 0,
373
+ retryDelay = 1e3,
374
+ timeout = 3e4
375
+ } = { __proto__: null, ...options };
376
+ let lastError;
377
+ for (let attempt = 0; attempt <= retries; attempt++) {
378
+ try {
379
+ return await httpRequestAttempt(url, {
380
+ body,
381
+ followRedirects,
382
+ headers,
383
+ maxRedirects,
384
+ method,
385
+ timeout
386
+ });
387
+ } catch (e) {
388
+ lastError = e;
389
+ if (attempt === retries) {
390
+ break;
391
+ }
392
+ const delayMs = retryDelay * 2 ** attempt;
393
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
394
+ }
395
+ }
396
+ throw lastError || new Error("Request failed after retries");
397
+ }
398
+ async function httpText(url, options) {
399
+ const {
400
+ body,
401
+ headers = {},
402
+ ...restOptions
403
+ } = {
404
+ __proto__: null,
405
+ ...options
406
+ };
407
+ const defaultHeaders = {
408
+ Accept: "text/plain"
409
+ };
410
+ if (body) {
411
+ defaultHeaders["Content-Type"] = "text/plain";
412
+ }
413
+ const mergedHeaders = {
414
+ ...defaultHeaders,
415
+ ...headers
416
+ };
417
+ const response = await httpRequest(url, {
418
+ body,
419
+ headers: mergedHeaders,
420
+ ...restOptions
421
+ });
378
422
  if (!response.ok) {
379
423
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
380
424
  }
@@ -383,7 +427,7 @@ async function httpGetText(url, options) {
383
427
  // Annotate the CommonJS export names for ESM import in node:
384
428
  0 && (module.exports = {
385
429
  httpDownload,
386
- httpGetJson,
387
- httpGetText,
388
- httpRequest
430
+ httpJson,
431
+ httpRequest,
432
+ httpText
389
433
  });
@@ -43,7 +43,7 @@ var import_strings = require("../strings");
43
43
  function getRepoUrlDetails(repoUrl = "") {
44
44
  const userAndRepo = repoUrl.replace(/^.+github.com\//, "").split("/");
45
45
  const user = userAndRepo[0] || "";
46
- const project = userAndRepo.length > 1 ? userAndRepo[1]?.slice(0, -".git".length) || "" : "";
46
+ const project = userAndRepo.length > 1 ? (userAndRepo[1]?.endsWith(".git") ? userAndRepo[1].slice(0, -4) : userAndRepo[1]) || "" : "";
47
47
  return { user, project };
48
48
  }
49
49
  // @__NO_SIDE_EFFECTS__
@@ -68,6 +68,24 @@ export declare const SOCKET_BTM_REPO: {
68
68
  readonly owner: "SocketDev";
69
69
  readonly repo: "socket-btm";
70
70
  };
71
+ /**
72
+ * Create a matcher function for a pattern using picomatch for glob patterns
73
+ * or simple prefix/suffix matching for object patterns.
74
+ *
75
+ * @param pattern - Pattern to match (string glob, prefix/suffix object, or RegExp)
76
+ * @returns Function that tests if a string matches the pattern
77
+ */
78
+ export declare function createAssetMatcher(pattern: string | {
79
+ prefix: string;
80
+ suffix: string;
81
+ } | RegExp): (input: string) => boolean;
82
+ /**
83
+ * Download a binary from any GitHub repository with version caching.
84
+ *
85
+ * @param config - Download configuration
86
+ * @returns Path to the downloaded binary
87
+ */
88
+ export declare function downloadGitHubRelease(config: DownloadGitHubReleaseConfig): Promise<string>;
71
89
  /**
72
90
  * Download a specific release asset.
73
91
  * Supports pattern matching for dynamic asset discovery.
@@ -115,10 +133,3 @@ export declare function getLatestRelease(toolPrefix: string, repoConfig: RepoCon
115
133
  export declare function getReleaseAssetUrl(tag: string, assetPattern: string | AssetPattern, repoConfig: RepoConfig, options?: {
116
134
  quiet?: boolean;
117
135
  }): Promise<string | null>;
118
- /**
119
- * Download a binary from any GitHub repository with version caching.
120
- *
121
- * @param config - Download configuration
122
- * @returns Path to the downloaded binary
123
- */
124
- export declare function downloadGitHubRelease(config: DownloadGitHubReleaseConfig): Promise<string>;