mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-17 00:16:46 +00:00
- Add backend field to BinaryManagerConfig type for installer context - Thread backend parameter through tar/zip extractors to use correct names - Delete existing binary before install to prevent mismatched binaries - Track backend in session metadata for debugging/monitoring - Validate and preserve backend field in config mergeWithDefaults - Pass backend to registerSession for session tracking The core issue was extractors calling getExecutableName() without the backend parameter, causing it to default to 'plus' regardless of user selection. This resulted in wrong binaries being extracted/renamed.
105 lines
3.5 KiB
TypeScript
105 lines
3.5 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, DEFAULT_BACKEND } from '../platform-detector';
|
|
import type { CLIProxyBackend } from '../types';
|
|
|
|
/**
|
|
* Extract tar.gz archive using Node.js built-in modules
|
|
*/
|
|
export function extractTarGz(
|
|
archivePath: string,
|
|
destDir: string,
|
|
verbose = false,
|
|
backend: CLIProxyBackend = DEFAULT_BACKEND
|
|
): Promise<void> {
|
|
return new Promise((resolve, reject) => {
|
|
const execName = getExecutableName(backend);
|
|
const archiveBinaryName = getArchiveBinaryName(backend);
|
|
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);
|
|
});
|
|
}
|