@rigour-labs/core 5.2.4 → 5.2.6

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.
@@ -13,5 +13,11 @@ export declare class SidecarProvider implements InferenceProvider {
13
13
  private getPlatformKey;
14
14
  private getPlatformPackageName;
15
15
  private resolveBinaryPath;
16
+ /**
17
+ * Download llama-cli directly from llama.cpp GitHub releases.
18
+ * Extracts the binary to ~/.rigour/bin/llama-cli
19
+ */
20
+ private downloadLlamaCli;
21
+ /** Legacy: install via npm brain package */
16
22
  private installSidecarBinary;
17
23
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Sidecar Binary Provider — runs inference via pre-compiled llama.cpp binary.
3
- * Binary ships as @rigour-labs/brain-{platform} optional npm dependency.
4
- * Falls back to PATH lookup for development/manual installs.
3
+ * Auto-downloads llama-cli from GitHub releases on first use.
4
+ * Falls back to npm packages or PATH lookup for development/manual installs.
5
5
  */
6
6
  import { execFile } from 'child_process';
7
7
  import { promisify } from 'util';
@@ -13,7 +13,8 @@ import { ensureModel, isModelCached, getModelInfo } from './model-manager.js';
13
13
  import { ensureExecutableBinary } from './executable.js';
14
14
  const execFileAsync = promisify(execFile);
15
15
  const SIDECAR_INSTALL_DIR = path.join(os.homedir(), '.rigour', 'sidecar');
16
- /** Platform npm package mapping */
16
+ const BINARY_DIR = path.join(os.homedir(), '.rigour', 'bin');
17
+ /** Platform → npm package mapping (legacy, still checked) */
17
18
  const PLATFORM_PACKAGES = {
18
19
  'darwin-arm64': '@rigour-labs/brain-darwin-arm64',
19
20
  'darwin-x64': '@rigour-labs/brain-darwin-x64',
@@ -21,6 +22,16 @@ const PLATFORM_PACKAGES = {
21
22
  'linux-arm64': '@rigour-labs/brain-linux-arm64',
22
23
  'win32-x64': '@rigour-labs/brain-win-x64',
23
24
  };
25
+ const LLAMA_RELEASE_TAG = 'b5604';
26
+ const LLAMA_RELEASE_BASE = `https://github.com/ggml-org/llama.cpp/releases/download/${LLAMA_RELEASE_TAG}`;
27
+ /** Platform → llama.cpp release asset name */
28
+ const LLAMA_RELEASE_ASSETS = {
29
+ 'darwin-arm64': `llama-${LLAMA_RELEASE_TAG}-bin-macos-arm64.zip`,
30
+ 'darwin-x64': `llama-${LLAMA_RELEASE_TAG}-bin-macos-x64.zip`,
31
+ 'linux-x64': `llama-${LLAMA_RELEASE_TAG}-bin-ubuntu-x64.zip`,
32
+ 'win32-x64': `llama-${LLAMA_RELEASE_TAG}-bin-win-cpu-x64.zip`,
33
+ // linux-arm64 not published by llama.cpp — users must build from source or use cloud provider
34
+ };
24
35
  export class SidecarProvider {
25
36
  name = 'sidecar';
26
37
  binaryPath = null;
@@ -36,26 +47,35 @@ export class SidecarProvider {
36
47
  return binary !== null;
37
48
  }
38
49
  async setup(onProgress) {
39
- const packageName = this.getPlatformPackageName();
40
50
  // 1. Check/resolve binary
41
51
  this.binaryPath = await this.resolveBinaryPath();
42
- // Auto-bootstrap local sidecar when missing.
43
- if (!this.binaryPath && packageName) {
44
- const installed = await this.installSidecarBinary(packageName, onProgress);
45
- if (installed) {
52
+ // Auto-bootstrap: download llama-cli directly from GitHub releases
53
+ if (!this.binaryPath) {
54
+ const downloaded = await this.downloadLlamaCli(onProgress);
55
+ if (downloaded) {
46
56
  this.binaryPath = await this.resolveBinaryPath();
47
57
  }
48
58
  }
59
+ // Legacy fallback: try npm package install
60
+ if (!this.binaryPath) {
61
+ const packageName = this.getPlatformPackageName();
62
+ if (packageName) {
63
+ const installed = await this.installSidecarBinary(packageName, onProgress);
64
+ if (installed) {
65
+ this.binaryPath = await this.resolveBinaryPath();
66
+ }
67
+ }
68
+ }
49
69
  if (!this.binaryPath) {
50
- onProgress?.('⚠ Inference engine not found. Install @rigour-labs/brain-* or add llama-cli to PATH');
51
- const installHint = packageName || `@rigour-labs/brain-${this.getPlatformKey()}`;
52
- throw new Error(`Sidecar binary not found. Run: npm install ${installHint}`);
70
+ onProgress?.('⚠ Inference engine not found. Rigour will auto-download on next attempt.');
71
+ throw new Error(`Sidecar binary not found. Check network connection and retry.`);
53
72
  }
54
73
  let executableCheck = ensureExecutableBinary(this.binaryPath);
55
74
  // If the discovered binary is not executable, try a managed reinstall once.
56
- if (!executableCheck.ok && packageName) {
75
+ const retryPackage = this.getPlatformPackageName();
76
+ if (!executableCheck.ok && retryPackage) {
57
77
  onProgress?.('⚠ Inference engine is present but not executable. Reinstalling managed sidecar...');
58
- const installed = await this.installSidecarBinary(packageName, onProgress);
78
+ const installed = await this.installSidecarBinary(retryPackage, onProgress);
59
79
  if (installed) {
60
80
  const refreshedPath = await this.resolveBinaryPath();
61
81
  if (refreshedPath) {
@@ -172,6 +192,12 @@ export class SidecarProvider {
172
192
  }
173
193
  async resolveBinaryPath() {
174
194
  const platformKey = this.getPlatformKey();
195
+ // Strategy 0: Check ~/.rigour/bin/llama-cli (auto-downloaded from GitHub releases)
196
+ const binaryName = os.platform() === 'win32' ? 'llama-cli.exe' : 'llama-cli';
197
+ const autoDownloadedPath = path.join(BINARY_DIR, binaryName);
198
+ if (await fs.pathExists(autoDownloadedPath)) {
199
+ return autoDownloadedPath;
200
+ }
175
201
  // Strategy 1: Check @rigour-labs/brain-{platform} optional dependency
176
202
  const packageName = PLATFORM_PACKAGES[platformKey];
177
203
  if (packageName) {
@@ -264,8 +290,73 @@ export class SidecarProvider {
264
290
  }
265
291
  return null;
266
292
  }
293
+ /**
294
+ * Download llama-cli directly from llama.cpp GitHub releases.
295
+ * Extracts the binary to ~/.rigour/bin/llama-cli
296
+ */
297
+ async downloadLlamaCli(onProgress) {
298
+ const platformKey = this.getPlatformKey();
299
+ const assetName = LLAMA_RELEASE_ASSETS[platformKey];
300
+ if (!assetName)
301
+ return false;
302
+ const url = `${LLAMA_RELEASE_BASE}/${assetName}`;
303
+ const zipPath = path.join(BINARY_DIR, assetName);
304
+ const binaryName = os.platform() === 'win32' ? 'llama-cli.exe' : 'llama-cli';
305
+ const destPath = path.join(BINARY_DIR, binaryName);
306
+ // Already downloaded
307
+ if (await fs.pathExists(destPath))
308
+ return true;
309
+ onProgress?.(`⬇ Downloading inference engine (llama.cpp ${LLAMA_RELEASE_TAG})...`);
310
+ try {
311
+ await fs.ensureDir(BINARY_DIR);
312
+ // Download zip
313
+ const response = await fetch(url, { redirect: 'follow' });
314
+ if (!response.ok) {
315
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
316
+ }
317
+ const buffer = Buffer.from(await response.arrayBuffer());
318
+ await fs.writeFile(zipPath, buffer);
319
+ onProgress?.(' Extracting...');
320
+ // Extract llama-cli from zip
321
+ if (os.platform() === 'win32') {
322
+ await execFileAsync('powershell', [
323
+ '-Command',
324
+ `Expand-Archive -Path "${zipPath}" -DestinationPath "${BINARY_DIR}/llama-extract" -Force`,
325
+ ], { timeout: 60000 });
326
+ }
327
+ else {
328
+ await execFileAsync('unzip', ['-o', zipPath, '-d', path.join(BINARY_DIR, 'llama-extract')], {
329
+ timeout: 60000,
330
+ });
331
+ }
332
+ // Find llama-cli in extracted files
333
+ const extractDir = path.join(BINARY_DIR, 'llama-extract');
334
+ const llamaBin = await findFileRecursive(extractDir, binaryName);
335
+ if (!llamaBin) {
336
+ throw new Error(`${binaryName} not found in release archive`);
337
+ }
338
+ await fs.copy(llamaBin, destPath);
339
+ if (os.platform() !== 'win32') {
340
+ await fs.chmod(destPath, 0o755);
341
+ }
342
+ // Cleanup
343
+ await fs.remove(zipPath);
344
+ await fs.remove(extractDir);
345
+ onProgress?.(`✓ Inference engine ready (${LLAMA_RELEASE_TAG})`);
346
+ return true;
347
+ }
348
+ catch (error) {
349
+ const reason = typeof error?.message === 'string' ? error.message : 'unknown error';
350
+ onProgress?.(`⚠ Download failed: ${reason}`);
351
+ // Cleanup partial downloads
352
+ await fs.remove(zipPath).catch(() => { });
353
+ await fs.remove(path.join(BINARY_DIR, 'llama-extract')).catch(() => { });
354
+ return false;
355
+ }
356
+ }
357
+ /** Legacy: install via npm brain package */
267
358
  async installSidecarBinary(packageName, onProgress) {
268
- onProgress?.(`⬇ Inference engine missing. Attempting automatic install: ${packageName}`);
359
+ onProgress?.(`⬇ Trying npm fallback: ${packageName}`);
269
360
  try {
270
361
  await fs.ensureDir(SIDECAR_INSTALL_DIR);
271
362
  await execFileAsync(os.platform() === 'win32' ? 'npm.cmd' : 'npm', ['install', '--no-save', '--no-package-lock', '--prefix', SIDECAR_INSTALL_DIR, packageName], {
@@ -276,7 +367,7 @@ export class SidecarProvider {
276
367
  }
277
368
  catch (error) {
278
369
  const reason = typeof error?.message === 'string' ? error.message : 'unknown install error';
279
- onProgress?.(`⚠ Auto-install failed: ${reason}`);
370
+ onProgress?.(`⚠ npm install failed: ${reason}`);
280
371
  return false;
281
372
  }
282
373
  onProgress?.(`✓ Installed ${packageName}`);
@@ -286,3 +377,19 @@ export class SidecarProvider {
286
377
  function quoteCmdArg(value) {
287
378
  return `"${value.replace(/"/g, '\\"')}"`;
288
379
  }
380
+ /** Recursively find a file by name in a directory */
381
+ async function findFileRecursive(dir, filename) {
382
+ const entries = await fs.readdir(dir, { withFileTypes: true });
383
+ for (const entry of entries) {
384
+ const fullPath = path.join(dir, entry.name);
385
+ if (entry.isDirectory()) {
386
+ const found = await findFileRecursive(fullPath, filename);
387
+ if (found)
388
+ return found;
389
+ }
390
+ else if (entry.name === filename) {
391
+ return fullPath;
392
+ }
393
+ }
394
+ return null;
395
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rigour-labs/core",
3
- "version": "5.2.4",
3
+ "version": "5.2.6",
4
4
  "description": "AI-native quality gate engine with local Bayesian learning. AST analysis, drift detection, Fix Packet generation, and agent self-healing across TypeScript, JavaScript, Python, Go, Ruby, and C#.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://rigour.run",
@@ -66,11 +66,11 @@
66
66
  "@xenova/transformers": "^2.17.2",
67
67
  "sqlite3": "^5.1.7",
68
68
  "openai": "^4.104.0",
69
- "@rigour-labs/brain-darwin-arm64": "5.2.4",
70
- "@rigour-labs/brain-darwin-x64": "5.2.4",
71
- "@rigour-labs/brain-linux-arm64": "5.2.4",
72
- "@rigour-labs/brain-linux-x64": "5.2.4",
73
- "@rigour-labs/brain-win-x64": "5.2.4"
69
+ "@rigour-labs/brain-darwin-x64": "5.2.6",
70
+ "@rigour-labs/brain-linux-x64": "5.2.6",
71
+ "@rigour-labs/brain-darwin-arm64": "5.2.6",
72
+ "@rigour-labs/brain-win-x64": "5.2.6",
73
+ "@rigour-labs/brain-linux-arm64": "5.2.6"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@types/fs-extra": "^11.0.4",