mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-17 00:16:46 +00:00
- extract types, version-checker, lifecycle, updater, installer - extract downloader (verifier), extractors (tar, zip), version-cache - slim binary-manager.ts from 1080 to 137 lines (87% reduction) - add barrel export at binary/index.ts
99 lines
3.4 KiB
TypeScript
99 lines
3.4 KiB
TypeScript
/**
|
|
* Tar.gz Archive Extractor
|
|
* Handles extraction of tar.gz archives using Node.js built-in modules.
|
|
*/
|
|
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import * as zlib from 'zlib';
|
|
import { getExecutableName, getArchiveBinaryName } from '../platform-detector';
|
|
|
|
/**
|
|
* Extract tar.gz archive using Node.js built-in modules
|
|
*/
|
|
export function extractTarGz(archivePath: string, destDir: string, verbose = false): Promise<void> {
|
|
return new Promise((resolve, reject) => {
|
|
const execName = getExecutableName();
|
|
const archiveBinaryName = getArchiveBinaryName();
|
|
const gunzip = zlib.createGunzip();
|
|
const input = fs.createReadStream(archivePath);
|
|
|
|
let headerBuffer = Buffer.alloc(0);
|
|
let currentFile: { name: string; size: number } | null = null;
|
|
let bytesRead = 0;
|
|
let fileBuffer = Buffer.alloc(0);
|
|
|
|
const processData = (data: Buffer) => {
|
|
headerBuffer = Buffer.concat([headerBuffer, data]);
|
|
|
|
while (headerBuffer.length >= 512) {
|
|
if (!currentFile) {
|
|
const header = headerBuffer.subarray(0, 512);
|
|
headerBuffer = headerBuffer.subarray(512);
|
|
|
|
if (header.every((b) => b === 0)) return;
|
|
|
|
let name = '';
|
|
for (let i = 0; i < 100 && header[i] !== 0; i++) {
|
|
name += String.fromCharCode(header[i]);
|
|
}
|
|
|
|
let sizeStr = '';
|
|
for (let i = 124; i < 136 && header[i] !== 0; i++) {
|
|
sizeStr += String.fromCharCode(header[i]);
|
|
}
|
|
const size = parseInt(sizeStr.trim(), 8) || 0;
|
|
|
|
if (name && size > 0) {
|
|
const baseName = path.basename(name);
|
|
if (
|
|
baseName === execName ||
|
|
baseName === archiveBinaryName ||
|
|
baseName === 'cli-proxy-api'
|
|
) {
|
|
currentFile = { name: baseName, size };
|
|
fileBuffer = Buffer.alloc(0);
|
|
bytesRead = 0;
|
|
} else {
|
|
const paddedSize = Math.ceil(size / 512) * 512;
|
|
if (headerBuffer.length >= paddedSize) {
|
|
headerBuffer = headerBuffer.subarray(paddedSize);
|
|
} else {
|
|
currentFile = { name: '', size: paddedSize };
|
|
bytesRead = 0;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
const remaining = currentFile.size - bytesRead;
|
|
const chunk = headerBuffer.subarray(0, Math.min(remaining, headerBuffer.length));
|
|
headerBuffer = headerBuffer.subarray(chunk.length);
|
|
|
|
if (currentFile.name) fileBuffer = Buffer.concat([fileBuffer, chunk]);
|
|
bytesRead += chunk.length;
|
|
|
|
if (bytesRead >= currentFile.size) {
|
|
if (currentFile.name) {
|
|
const destPath = path.join(destDir, execName);
|
|
fs.writeFileSync(destPath, fileBuffer);
|
|
if (verbose)
|
|
console.error(`[cliproxy] Extracted: ${currentFile.name} -> ${destPath}`);
|
|
}
|
|
const paddedSize = Math.ceil(currentFile.size / 512) * 512;
|
|
const padding = paddedSize - currentFile.size;
|
|
if (headerBuffer.length >= padding) headerBuffer = headerBuffer.subarray(padding);
|
|
currentFile = null;
|
|
fileBuffer = Buffer.alloc(0);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
input.pipe(gunzip);
|
|
gunzip.on('data', processData);
|
|
gunzip.on('end', resolve);
|
|
gunzip.on('error', reject);
|
|
input.on('error', reject);
|
|
});
|
|
}
|