Skip to content

Commit b08e12a

Browse files
committed
Fix ubuntu issues
1 parent 602aa79 commit b08e12a

1 file changed

Lines changed: 99 additions & 3 deletions

File tree

src/llamaSetup.ts

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,9 +222,9 @@ function pickAssetNamePattern(platform: Platform, arch: Arch): (name: string) =>
222222
}
223223

224224
// Strict: only Ubuntu Vulkan x64 builds:
225-
// llama-b*-bin-ubuntu-vulkan-x64.zip
225+
// llama-b*-bin-ubuntu-vulkan-x64.tar.gz
226226
if (
227-
lower.endsWith('.zip') &&
227+
lower.endsWith('.tar.gz') &&
228228
lower.includes('-ubuntu-vulkan-x64')
229229
) {
230230
return true;
@@ -411,7 +411,8 @@ async function downloadFile(
411411
/**
412412
* Extract a zip archive into targetDir and resolve when complete.
413413
* Uses native unzip on macOS to preserve file permissions and attributes.
414-
* Uses "unzipper" package on Linux and Windows.
414+
* Uses "unzipper" package on Windows.
415+
* Note: Ubuntu uses tar.gz archives, not zip (see extractTarGz function).
415416
*/
416417
async function extractZip(assetPath: string, targetDir: string): Promise<void> {
417418
ensureDirSync(targetDir);
@@ -460,6 +461,33 @@ async function extractZip(assetPath: string, targetDir: string): Promise<void> {
460461
});
461462
}
462463

464+
/**
465+
* Extract a tar.gz archive into targetDir using native tar command.
466+
* This is used for Ubuntu builds which are distributed as .tar.gz files.
467+
*/
468+
async function extractTarGz(assetPath: string, targetDir: string): Promise<void> {
469+
ensureDirSync(targetDir);
470+
logDebug('Extracting llama.cpp tar.gz archive', { assetPath, targetDir });
471+
472+
// eslint-disable-next-line @typescript-eslint/no-var-requires
473+
const { execFile } = require('child_process');
474+
// eslint-disable-next-line @typescript-eslint/no-var-requires
475+
const { promisify } = require('util');
476+
const execFileAsync = promisify(execFile);
477+
478+
try {
479+
// -x: extract
480+
// -z: decompress with gzip
481+
// -f: file
482+
// -C: change to directory
483+
await execFileAsync('tar', ['-xzf', assetPath, '-C', targetDir]);
484+
logDebug('Tar.gz extraction completed', { assetPath, targetDir });
485+
} catch (err) {
486+
logError('Tar extraction failed', err);
487+
throw new Error(`Failed to extract tar.gz: ${err}`);
488+
}
489+
}
490+
463491
/**
464492
* Given the downloaded asset and platform, locate the llama-server binary.
465493
*
@@ -469,6 +497,7 @@ async function extractZip(assetPath: string, targetDir: string): Promise<void> {
469497
* Behavior:
470498
* - If asset is a Windows .exe, return as-is.
471499
* - If asset is a .zip, extract into INSTALL_DIR and search for llama-server.
500+
* - If asset is a .tar.gz (Ubuntu), extract using tar command and search for llama-server.
472501
* - Ensure the found binary is marked executable.
473502
* - Otherwise, fall back to assetPath to match previous behavior.
474503
*/
@@ -550,6 +579,73 @@ async function inferBinaryPathFromAsset(
550579
throw new Error(`llama-server binary not found in extracted archive: ${assetPath}`);
551580
}
552581

582+
// Tar.gz archives: extract then search for llama-server
583+
if (lower.endsWith('.tar.gz')) {
584+
const extractRoot = INSTALL_DIR;
585+
await extractTarGz(assetPath, extractRoot);
586+
587+
// Expected: llama-{version}-bin-ubuntu-vulkan-x64/build/bin/llama-server
588+
const entries = fs.readdirSync(extractRoot);
589+
for (const entry of entries) {
590+
const full = path.join(extractRoot, entry);
591+
if (!fs.statSync(full).isDirectory()) continue;
592+
593+
const buildBin = path.join(full, 'build', 'bin');
594+
if (fs.existsSync(buildBin) && fs.statSync(buildBin).isDirectory()) {
595+
const binEntries = fs.readdirSync(buildBin);
596+
for (const be of binEntries) {
597+
const candidate = path.join(buildBin, be);
598+
const name = be.toLowerCase();
599+
if (
600+
fs.statSync(candidate).isFile() &&
601+
(name === 'llama-server' || name === 'llama-server.exe')
602+
) {
603+
try {
604+
fs.chmodSync(candidate, 0o755);
605+
} catch {
606+
// best-effort; ignore chmod failures on non-POSIX
607+
}
608+
logDebug('Resolved llama-server binary inside archive', {
609+
assetPath,
610+
candidate,
611+
});
612+
return candidate;
613+
}
614+
}
615+
}
616+
}
617+
618+
// Fallback: recursive search under extractRoot
619+
const stack: string[] = [extractRoot];
620+
while (stack.length) {
621+
const dir = stack.pop() as string;
622+
const children = fs.readdirSync(dir);
623+
for (const child of children) {
624+
const full = path.join(dir, child);
625+
const stat = fs.statSync(full);
626+
if (stat.isDirectory()) {
627+
stack.push(full);
628+
} else if (stat.isFile()) {
629+
const name = child.toLowerCase();
630+
if (name === 'llama-server' || name === 'llama-server.exe') {
631+
try {
632+
fs.chmodSync(full, 0o755);
633+
} catch {
634+
// ignore chmod errors
635+
}
636+
logDebug('Resolved llama-server binary via recursive search', {
637+
assetPath,
638+
candidate: full,
639+
});
640+
return full;
641+
}
642+
}
643+
}
644+
}
645+
646+
throw new Error(`llama-server binary not found in extracted archive: ${assetPath}`);
647+
}
648+
553649
// Non-zip assets: preserve previous behavior.
554650
return assetPath;
555651
}

0 commit comments

Comments
 (0)