@redpanda-data/docs-extensions-and-macros 4.13.1 โ†’ 4.13.2

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.
Files changed (33) hide show
  1. package/bin/doc-tools-mcp.js +15 -3
  2. package/bin/doc-tools.js +767 -2088
  3. package/bin/mcp-tools/property-docs.js +18 -0
  4. package/bin/mcp-tools/rpcn-docs.js +28 -3
  5. package/cli-utils/antora-utils.js +53 -2
  6. package/cli-utils/dependencies.js +313 -0
  7. package/cli-utils/diff-utils.js +273 -0
  8. package/cli-utils/doc-tools-utils.js +54 -0
  9. package/extensions/algolia-indexer/generate-index.js +134 -102
  10. package/extensions/algolia-indexer/index.js +70 -38
  11. package/extensions/collect-bloblang-samples.js +2 -1
  12. package/extensions/generate-rp-connect-categories.js +126 -67
  13. package/extensions/generate-rp-connect-info.js +291 -137
  14. package/macros/rp-connect-components.js +34 -5
  15. package/package.json +4 -3
  16. package/tools/add-commercial-names.js +207 -0
  17. package/tools/generate-cli-docs.js +6 -2
  18. package/tools/get-console-version.js +5 -0
  19. package/tools/get-redpanda-version.js +5 -0
  20. package/tools/property-extractor/compare-properties.js +3 -3
  21. package/tools/property-extractor/generate-handlebars-docs.js +14 -14
  22. package/tools/property-extractor/generate-pr-summary.js +46 -0
  23. package/tools/property-extractor/pr-summary-formatter.js +375 -0
  24. package/tools/redpanda-connect/README.adoc +403 -38
  25. package/tools/redpanda-connect/connector-binary-analyzer.js +588 -0
  26. package/tools/redpanda-connect/generate-rpcn-connector-docs.js +97 -34
  27. package/tools/redpanda-connect/parse-csv-connectors.js +1 -1
  28. package/tools/redpanda-connect/pr-summary-formatter.js +601 -0
  29. package/tools/redpanda-connect/report-delta.js +69 -2
  30. package/tools/redpanda-connect/rpcn-connector-docs-handler.js +1180 -0
  31. package/tools/redpanda-connect/templates/connector.hbs +38 -0
  32. package/tools/redpanda-connect/templates/intro.hbs +0 -20
  33. package/tools/redpanda-connect/update-nav.js +205 -0
@@ -0,0 +1,588 @@
1
+ const { Octokit } = require('@octokit/rest');
2
+ const { execSync, spawnSync } = require('child_process');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+ const https = require('https');
7
+
8
+ /**
9
+ * Binary analyzer for Redpanda Connect
10
+ * Analyzes OSS, Cloud, and cgo binaries to determine:
11
+ * - Which connectors are available in Cloud (cloud support)
12
+ * - Which connectors require cgo builds (cgo-only)
13
+ * - Which connectors are self-hosted only
14
+ */
15
+
16
+ // Initialize Octokit with optional authentication
17
+ const octokit = new Octokit({
18
+ auth: process.env.GITHUB_TOKEN,
19
+ userAgent: 'redpanda-docs-tools',
20
+ retry: {
21
+ enabled: true,
22
+ retries: 3
23
+ }
24
+ });
25
+
26
+ const REPO_OWNER = 'redpanda-data';
27
+ const REPO_NAME = 'connect';
28
+
29
+ /**
30
+ * Get platform-specific binary name
31
+ * @returns {Object} Platform and arch info
32
+ */
33
+ function getPlatformInfo() {
34
+ const os = require('os');
35
+ const platform = os.platform();
36
+ const arch = os.arch();
37
+
38
+ // Map Node.js platform names to binary names
39
+ const platformMap = {
40
+ 'darwin': 'darwin',
41
+ 'linux': 'linux',
42
+ 'win32': 'windows'
43
+ };
44
+
45
+ const archMap = {
46
+ 'x64': 'amd64',
47
+ 'arm64': 'arm64',
48
+ 'arm': 'arm64'
49
+ };
50
+
51
+ return {
52
+ platform: platformMap[platform] || 'linux',
53
+ arch: archMap[arch] || 'amd64'
54
+ };
55
+ }
56
+
57
+ /**
58
+ * Get the latest release version from GitHub
59
+ * @param {string} binaryType - Type of binary ('cloud' or 'cgo')
60
+ * @returns {Promise<string>} Version string (e.g., "4.76.0")
61
+ */
62
+ async function getLatestVersion(binaryType = 'cloud') {
63
+ try {
64
+ const { data: releases } = await octokit.repos.listReleases({
65
+ owner: REPO_OWNER,
66
+ repo: REPO_NAME,
67
+ per_page: 20
68
+ });
69
+
70
+ const prefix = binaryType === 'cloud' ? 'redpanda-connect-cloud' : 'redpanda-connect-cgo';
71
+
72
+ // Find the latest release with the specified binary type
73
+ for (const release of releases) {
74
+ const asset = release.assets.find(a =>
75
+ a.name.startsWith(prefix) &&
76
+ a.name.includes('linux') &&
77
+ a.name.includes('amd64')
78
+ );
79
+
80
+ if (asset) {
81
+ const version = release.tag_name.replace(/^v/, '');
82
+ return version;
83
+ }
84
+ }
85
+
86
+ throw new Error(`No ${binaryType} binary found in recent releases`);
87
+ } catch (error) {
88
+ if (error.status === 403 && error.message.includes('rate limit')) {
89
+ throw new Error(
90
+ 'GitHub API rate limit exceeded. Set GITHUB_TOKEN environment variable to increase limit from 60 to 5000 requests/hour.'
91
+ );
92
+ }
93
+ throw new Error(`Failed to fetch latest version: ${error.message}`);
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Download a file from a URL
99
+ * @param {string} url - URL to download
100
+ * @param {string} destPath - Destination file path
101
+ * @returns {Promise<void>}
102
+ */
103
+ function downloadFile(url, destPath) {
104
+ return new Promise((resolve, reject) => {
105
+ https.get(url, { headers: { 'User-Agent': 'redpanda-docs-tools' } }, (res) => {
106
+ // Follow redirects
107
+ if (res.statusCode === 302 || res.statusCode === 301) {
108
+ downloadFile(res.headers.location, destPath).then(resolve).catch(reject);
109
+ return;
110
+ }
111
+
112
+ if (res.statusCode !== 200) {
113
+ reject(new Error(`Failed to download: HTTP ${res.statusCode}`));
114
+ return;
115
+ }
116
+
117
+ const fileStream = fs.createWriteStream(destPath);
118
+ res.pipe(fileStream);
119
+
120
+ fileStream.on('finish', () => {
121
+ fileStream.close();
122
+ resolve();
123
+ });
124
+
125
+ fileStream.on('error', (err) => {
126
+ if (fs.existsSync(destPath)) {
127
+ fs.unlinkSync(destPath);
128
+ }
129
+ reject(err);
130
+ });
131
+ }).on('error', (err) => {
132
+ reject(err);
133
+ });
134
+ });
135
+ }
136
+
137
+ /**
138
+ * Download a binary from GitHub releases
139
+ * @param {string} binaryType - 'cloud' or 'cgo'
140
+ * @param {string} version - Version to download (e.g., "4.76.0")
141
+ * @param {string} destDir - Destination directory
142
+ * @returns {Promise<string>} Path to downloaded binary
143
+ */
144
+ async function downloadBinary(binaryType, version, destDir) {
145
+ // cgo binaries are only available for linux/amd64, so force that platform
146
+ const platformInfo = binaryType === 'cgo'
147
+ ? { platform: 'linux', arch: 'amd64' }
148
+ : getPlatformInfo();
149
+ const prefix = binaryType === 'cloud' ? 'redpanda-connect-cloud' : 'redpanda-connect-cgo';
150
+ const binaryName = `${prefix}-${version}-${platformInfo.platform}-${platformInfo.arch}`;
151
+ const destPath = path.join(destDir, binaryName);
152
+
153
+ // Skip download if already exists
154
+ if (fs.existsSync(destPath)) {
155
+ console.log(`โœ“ ${binaryType.toUpperCase()} binary already downloaded: ${binaryName}`);
156
+ return destPath;
157
+ }
158
+
159
+ console.log(`Downloading ${binaryType.toUpperCase()} binary v${version} for ${platformInfo.platform}/${platformInfo.arch}...`);
160
+
161
+ try {
162
+ // Get release information
163
+ const { data: release } = await octokit.repos.getReleaseByTag({
164
+ owner: REPO_OWNER,
165
+ repo: REPO_NAME,
166
+ tag: `v${version}`
167
+ });
168
+
169
+ // Find the asset
170
+ const assetName = `${prefix}_${version}_${platformInfo.platform}_${platformInfo.arch}.tar.gz`;
171
+ const asset = release.assets.find(a => a.name === assetName);
172
+
173
+ if (!asset) {
174
+ throw new Error(`${binaryType.toUpperCase()} binary not found for version ${version} (${assetName})`);
175
+ }
176
+
177
+ // Download the tarball
178
+ const tarballPath = `${destPath}.tar.gz`;
179
+ await downloadFile(asset.browser_download_url, tarballPath);
180
+
181
+ // Extract the binary
182
+ console.log(`Extracting ${binaryType.toUpperCase()} binary...`);
183
+ execSync(`tar -xzf "${tarballPath}" -C "${destDir}"`, { stdio: 'ignore' });
184
+
185
+ // Find the extracted binary
186
+ const extractedFiles = fs.readdirSync(destDir);
187
+ let binaryPath = null;
188
+
189
+ for (const file of extractedFiles) {
190
+ const fullPath = path.join(destDir, file);
191
+ if (fs.statSync(fullPath).isFile() && file.includes('redpanda-connect') && !file.endsWith('.tar.gz')) {
192
+ binaryPath = fullPath;
193
+ break;
194
+ }
195
+ }
196
+
197
+ if (!binaryPath) {
198
+ throw new Error('Binary not found after extraction');
199
+ }
200
+
201
+ // Rename to standard name if needed
202
+ if (binaryPath !== destPath) {
203
+ fs.renameSync(binaryPath, destPath);
204
+ }
205
+
206
+ // Make executable
207
+ fs.chmodSync(destPath, 0o755);
208
+
209
+ // Clean up tarball
210
+ fs.unlinkSync(tarballPath);
211
+
212
+ console.log(`Done: Downloaded ${binaryType.toUpperCase()} binary: ${binaryName}`);
213
+ return destPath;
214
+ } catch (error) {
215
+ if (error.status === 404) {
216
+ throw new Error(`Release v${version} not found or ${binaryType} binary not available for this version`);
217
+ } else if (error.status === 403) {
218
+ throw new Error('GitHub API rate limit exceeded. Set GITHUB_TOKEN environment variable.');
219
+ }
220
+ throw new Error(`Failed to download ${binaryType} binary: ${error.message}`);
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Download cloud binary from GitHub releases
226
+ * @param {string} version - Version to download
227
+ * @param {string} destDir - Destination directory
228
+ * @returns {Promise<string>} Path to downloaded binary
229
+ */
230
+ async function downloadCloudBinary(version, destDir) {
231
+ return downloadBinary('cloud', version, destDir);
232
+ }
233
+
234
+ /**
235
+ * Download cgo binary from GitHub releases
236
+ * @param {string} version - Version to download
237
+ * @param {string} destDir - Destination directory
238
+ * @returns {Promise<string>} Path to downloaded binary
239
+ */
240
+ async function downloadCgoBinary(version, destDir) {
241
+ return downloadBinary('cgo', version, destDir);
242
+ }
243
+
244
+ /**
245
+ * Get list of connectors from a binary
246
+ * @param {string} binaryPath - Path to binary
247
+ * @returns {object} Connector index (same format as rpk connect list --format json-full)
248
+ */
249
+ function getConnectorList(binaryPath) {
250
+ try {
251
+ const binaryName = path.basename(binaryPath);
252
+ console.log(`Inspecting ${binaryName} for connectors...`);
253
+
254
+ // Check if this is a Linux binary on non-Linux platform (need Docker)
255
+ const isLinuxBinary = binaryName.includes('linux');
256
+ const needsDocker = isLinuxBinary && os.platform() !== 'linux';
257
+
258
+ let result;
259
+ if (needsDocker) {
260
+ // Use Docker to run Linux binaries on macOS/Windows
261
+ const binaryDir = path.dirname(binaryPath);
262
+ const binaryFile = path.basename(binaryPath);
263
+
264
+ // Install dependencies for cgo binaries (libzmq) and run
265
+ const isCgoBinary = binaryFile.includes('cgo');
266
+ const command = isCgoBinary
267
+ ? `apt-get update -qq && apt-get install -qq -y libzmq5 > /dev/null 2>&1 && chmod +x ./${binaryFile} && ./${binaryFile} list --format json-full`
268
+ : `chmod +x ./${binaryFile} && ./${binaryFile} list --format json-full`;
269
+
270
+ result = spawnSync('docker', [
271
+ 'run', '--rm', '--platform', 'linux/amd64',
272
+ '-v', `${binaryDir}:/work`,
273
+ '-w', '/work',
274
+ 'ubuntu:22.04',
275
+ 'bash', '-c', command
276
+ ], {
277
+ stdio: ['ignore', 'pipe', 'ignore'],
278
+ maxBuffer: 10 * 1024 * 1024 // 10MB buffer
279
+ });
280
+ } else {
281
+ // Run natively
282
+ result = spawnSync(binaryPath, ['list', '--format', 'json-full'], {
283
+ stdio: ['ignore', 'pipe', 'ignore'],
284
+ maxBuffer: 10 * 1024 * 1024 // 10MB buffer
285
+ });
286
+ }
287
+
288
+ if (result.error) {
289
+ throw new Error(`Failed to execute binary: ${result.error.message}`);
290
+ }
291
+
292
+ if (result.status !== 0) {
293
+ throw new Error(`Binary exited with code ${result.status}`);
294
+ }
295
+
296
+ const output = result.stdout.toString();
297
+ const connectors = JSON.parse(output);
298
+
299
+ const typeCount = Object.keys(connectors).filter(k => Array.isArray(connectors[k])).length;
300
+ console.log(`Done: Found ${typeCount} connector types in ${binaryName}`);
301
+
302
+ return connectors;
303
+ } catch (err) {
304
+ throw new Error(`Failed to get connector list: ${err.message}`);
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Deprecated alias for backward compatibility
310
+ * @deprecated Use getConnectorList instead
311
+ */
312
+ function getCloudConnectors(binaryPath) {
313
+ return getConnectorList(binaryPath);
314
+ }
315
+
316
+ /**
317
+ * Build a set of connector keys for comparison
318
+ * @param {object} index - Connector index
319
+ * @returns {Set<string>} Set of "type:name" keys
320
+ */
321
+ function buildConnectorSet(index) {
322
+ const connectorSet = new Set();
323
+
324
+ // Iterate through all connector types
325
+ const types = Object.keys(index).filter(key => Array.isArray(index[key]));
326
+
327
+ types.forEach(type => {
328
+ index[type].forEach(connector => {
329
+ if (connector.name) {
330
+ connectorSet.add(`${type}:${connector.name}`);
331
+ }
332
+ });
333
+ });
334
+
335
+ return connectorSet;
336
+ }
337
+
338
+ /**
339
+ * Compare OSS connectors with cloud connectors
340
+ * @param {object} ossIndex - OSS connector index
341
+ * @param {object} cloudIndex - Cloud connector index
342
+ * @returns {object} Comparison results
343
+ */
344
+ function compareOssWithCloud(ossIndex, cloudIndex) {
345
+ const ossSet = buildConnectorSet(ossIndex);
346
+ const cloudSet = buildConnectorSet(cloudIndex);
347
+
348
+ const inCloud = [];
349
+ const notInCloud = [];
350
+ const cloudOnly = [];
351
+
352
+ // Find OSS connectors and check if they're in cloud
353
+ ossSet.forEach(key => {
354
+ const [type, name] = key.split(':');
355
+ const ossConnector = ossIndex[type]?.find(c => c.name === name);
356
+
357
+ if (cloudSet.has(key)) {
358
+ inCloud.push({
359
+ type,
360
+ name,
361
+ status: ossConnector?.status || ossConnector?.type || 'stable'
362
+ });
363
+ } else {
364
+ notInCloud.push({
365
+ type,
366
+ name,
367
+ status: ossConnector?.status || ossConnector?.type || 'stable',
368
+ reason: 'self-hosted-only'
369
+ });
370
+ }
371
+ });
372
+
373
+ // Find cloud-only connectors (in cloud but NOT in OSS)
374
+ cloudSet.forEach(key => {
375
+ if (!ossSet.has(key)) {
376
+ const [type, name] = key.split(':');
377
+ const cloudConnector = cloudIndex[type]?.find(c => c.name === name);
378
+
379
+ if (cloudConnector) {
380
+ cloudOnly.push({
381
+ ...cloudConnector, // Include full connector schema
382
+ type,
383
+ name,
384
+ status: cloudConnector.status || 'stable',
385
+ reason: 'cloud-only'
386
+ });
387
+ }
388
+ }
389
+ });
390
+
391
+ return {
392
+ inCloud,
393
+ notInCloud,
394
+ cloudOnly,
395
+ totalOss: ossSet.size,
396
+ totalCloud: cloudSet.size
397
+ };
398
+ }
399
+
400
+ /**
401
+ * Find connectors that are only available in cgo builds
402
+ * @param {object} ossIndex - OSS connector index (from rpk connect)
403
+ * @param {object} cgoIndex - cgo connector index
404
+ * @returns {Array<object>} List of cgo-only connectors
405
+ */
406
+ function findCgoOnlyConnectors(ossIndex, cgoIndex) {
407
+ const ossSet = buildConnectorSet(ossIndex);
408
+ const cgoSet = buildConnectorSet(cgoIndex);
409
+
410
+ const cgoOnly = [];
411
+
412
+ cgoSet.forEach(key => {
413
+ if (!ossSet.has(key)) {
414
+ const [type, name] = key.split(':');
415
+ const connector = cgoIndex[type]?.find(c => c.name === name);
416
+ if (connector) {
417
+ // Return the full connector object with all fields and examples
418
+ cgoOnly.push({
419
+ ...connector,
420
+ type, // Add type field for convenience
421
+ requiresCgo: true // Mark as cgo-required
422
+ });
423
+ }
424
+ }
425
+ });
426
+
427
+ return cgoOnly;
428
+ }
429
+
430
+ /**
431
+ * Analyze all binary types for connector support
432
+ * @param {string} ossVersion - OSS version to check
433
+ * @param {string} [cloudVersion] - Cloud version (auto-detected if not provided)
434
+ * @param {string} [dataDir] - Directory for docs-data (analysis results only)
435
+ * @param {object} [options] - Analysis options
436
+ * @param {boolean} [options.skipCloud] - Skip cloud binary analysis
437
+ * @param {boolean} [options.skipCgo] - Skip cgo binary analysis
438
+ * @param {string} [options.cgoVersion] - cgo version (defaults to cloudVersion)
439
+ * @returns {Promise<object>} Analysis results
440
+ */
441
+ async function analyzeAllBinaries(ossVersion, cloudVersion = null, dataDir = null, options = {}) {
442
+ const { skipCloud = false, skipCgo = false, cgoVersion = null } = options;
443
+
444
+ const docsDataDir = dataDir || path.resolve(process.cwd(), 'docs-data');
445
+
446
+ // Use temp directory for binaries (not docs-data)
447
+ const os = require('os');
448
+ const binaryDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rpcn-binaries-'));
449
+
450
+ console.log(`๐Ÿ“ Using temp directory for binaries: ${binaryDir}`);
451
+
452
+ // Load OSS connector data (from rpk connect list)
453
+ const ossDataPath = path.join(docsDataDir, `connect-${ossVersion}.json`);
454
+ if (!fs.existsSync(ossDataPath)) {
455
+ throw new Error(`OSS data file not found: ${ossDataPath}. Run 'rpk connect list --format json-full > ${ossDataPath}' first.`);
456
+ }
457
+
458
+ const ossIndex = JSON.parse(fs.readFileSync(ossDataPath, 'utf8'));
459
+
460
+ // Cloud binary analysis
461
+ let cloudIndex = null;
462
+ let comparison = null;
463
+
464
+ if (!skipCloud) {
465
+ // Auto-detect cloud version if not provided
466
+ if (!cloudVersion) {
467
+ console.log('Detecting latest cloud version...');
468
+ cloudVersion = await getLatestVersion('cloud');
469
+ console.log(`โœ“ Latest cloud version: ${cloudVersion}`);
470
+ }
471
+
472
+ // Download and inspect cloud binary
473
+ const cloudBinaryPath = await downloadCloudBinary(cloudVersion, binaryDir);
474
+ cloudIndex = getConnectorList(cloudBinaryPath);
475
+
476
+ // Compare OSS with Cloud
477
+ comparison = compareOssWithCloud(ossIndex, cloudIndex);
478
+ } else {
479
+ console.log('โฉ Skipping cloud binary analysis');
480
+ }
481
+
482
+ // cgo binary analysis
483
+ let cgoOnly = [];
484
+ let cgoIndex = null;
485
+
486
+ if (!skipCgo) {
487
+ console.log('\nAnalyzing cgo binary...');
488
+ try {
489
+ // Use cgo-specific version or fall back to cloud version
490
+ let effectiveCgoVersion = cgoVersion || cloudVersion;
491
+
492
+ // If neither is provided, auto-detect
493
+ if (!effectiveCgoVersion) {
494
+ console.log('Detecting latest cgo version...');
495
+ effectiveCgoVersion = await getLatestVersion('cgo');
496
+ console.log(`โœ“ Latest cgo version: ${effectiveCgoVersion}`);
497
+ }
498
+
499
+ const cgoBinaryPath = await downloadCgoBinary(effectiveCgoVersion, binaryDir);
500
+ cgoIndex = getConnectorList(cgoBinaryPath);
501
+
502
+ // Find connectors only in cgo (not in regular OSS binary)
503
+ // These are connectors that require cgo-enabled builds
504
+ cgoOnly = findCgoOnlyConnectors(ossIndex, cgoIndex);
505
+
506
+ console.log(`Done: Cgo analysis complete: ${cgoOnly.length} cgo-only connector(s) found`);
507
+ if (cgoOnly.length > 0) {
508
+ console.log(' cgo-only connectors:');
509
+ cgoOnly.forEach(c => console.log(` โ€ข ${c.type}/${c.name} (${c.status})`));
510
+ }
511
+ } catch (err) {
512
+ console.error(`Warning: Cgo analysis failed: ${err.message}`);
513
+ console.error(' Continuing without cgo data...');
514
+ }
515
+ } else {
516
+ console.log('โฉ Skipping cgo binary analysis');
517
+ }
518
+
519
+ // Clean up temp binaries
520
+ try {
521
+ fs.rmSync(binaryDir, { recursive: true, force: true });
522
+ console.log(`๐Ÿงน Cleaned up temp binaries`);
523
+ } catch (err) {
524
+ console.warn(`Warning: Could not clean up temp directory: ${err.message}`);
525
+ }
526
+
527
+ // IMPORTANT: cgoOnly contains connectors that exist ONLY in the cgo binary
528
+ // and NOT in the regular OSS binary (rpk connect list). These are the
529
+ // connectors that require cgo-enabled builds. Connectors available in the
530
+ // standard OSS binary do NOT require cgo and will NOT be in this list.
531
+ //
532
+ // As of v4.76.0, cgo-only connectors are:
533
+ // - inputs/tigerbeetle_cdc (beta)
534
+ // - processors/ffi (experimental)
535
+ return {
536
+ ossVersion,
537
+ cloudVersion,
538
+ cgoVersion: cgoVersion || cloudVersion, // The version analyzed for cgo
539
+ comparison,
540
+ cloudIndex,
541
+ cgoIndex,
542
+ cgoOnly
543
+ };
544
+ }
545
+
546
+ /**
547
+ * Deprecated alias for backward compatibility
548
+ * @deprecated Use analyzeAllBinaries instead
549
+ */
550
+ async function getCloudSupport(ossVersion, cloudVersion = null, dataDir = null) {
551
+ return analyzeAllBinaries(ossVersion, cloudVersion, dataDir);
552
+ }
553
+
554
+ /**
555
+ * Check GitHub API rate limit
556
+ * @returns {Promise<object>} Rate limit information
557
+ */
558
+ async function checkRateLimit() {
559
+ try {
560
+ const { data } = await octokit.rateLimit.get();
561
+ return {
562
+ remaining: data.rate.remaining,
563
+ limit: data.rate.limit,
564
+ reset: new Date(data.rate.reset * 1000),
565
+ used: data.rate.used
566
+ };
567
+ } catch (error) {
568
+ throw new Error(`Failed to check rate limit: ${error.message}`);
569
+ }
570
+ }
571
+
572
+ module.exports = {
573
+ // Primary functions
574
+ analyzeAllBinaries,
575
+ findCgoOnlyConnectors,
576
+ getLatestVersion,
577
+ downloadCloudBinary,
578
+ downloadCgoBinary,
579
+ getConnectorList,
580
+ buildConnectorSet,
581
+ compareOssWithCloud,
582
+ checkRateLimit,
583
+
584
+ // Deprecated aliases (for backward compatibility)
585
+ getCloudSupport,
586
+ getLatestCloudVersion: () => getLatestVersion('cloud'),
587
+ getCloudConnectors: getConnectorList
588
+ };