mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-29 01:41:42 +00:00
Some checks are pending
CI / quality (push) Waiting to run
CI / tests (push) Waiting to run
CI / e2e (push) Waiting to run
CI / scope-parity (push) Waiting to run
CI / Save PR Metadata (push) Blocked by required conditions
CI / CI Gate (push) Blocked by required conditions
Release Candidate / Check if release candidate should run (push) Waiting to run
Release Candidate / ci (push) Blocked by required conditions
Release Candidate / Publish release candidate to npm (push) Blocked by required conditions
Release Candidate / Build & Push RC Docker images (push) Blocked by required conditions
* fix(lbug): bound DuckDB extension install via ExtensionManager (closes #1128) `gitnexus analyze` could hang indefinitely (60% / 85% on Windows) when DuckDB's `INSTALL fts` or `INSTALL VECTOR` was unable to reach `extensions.duckdb.org`. The DuckDB driver's INSTALL is a synchronous network call, so any blocked egress would block the Node event loop forever. Replace the ad-hoc, in-process INSTALL/LOAD scattered across `lbug-adapter.ts` and `pool-adapter.ts` with a single `ExtensionManager` that owns the lifecycle of optional DuckDB extensions: * `LOAD` is always tried first — per-connection, idempotent, no network. * If `LOAD` fails and policy permits, INSTALL runs in a short-lived child Node process bounded by `GITNEXUS_LBUG_EXTENSION_INSTALL_TIMEOUT_MS` (default 15s). The parent loop keeps spinning; on timeout the child is killed with SIGKILL and the capability is flagged unavailable. * Capabilities and install attempts are cached per process, so a single bounded install per extension covers every subsequent call. Install policy is now an explicit, per-context decision: * `auto` (default for analyze) — try LOAD, fall back to bounded INSTALL. * `load-only` — used by `pool-adapter` (serve / MCP read paths) so user queries never block on a network install. * `never` — operator escape hatch for offline / airgapped environments. `createFTSIndex` and `createVectorIndex` now check the boolean return value before issuing the index DDL, so missing extensions degrade BM25 and semantic search gracefully without ever throwing during analyze. Tests: - New unit suite for `ExtensionManager` covering LOAD-first behavior, all three policies, install caching, observability, and warn dedup. - Existing vector-extension integration tests pass against the new boolean return type. - Existing embedding-pipeline mocks updated to return `true`. Docs: `gitnexus/README.md` documents `GITNEXUS_LBUG_EXTENSION_INSTALL` and `GITNEXUS_LBUG_EXTENSION_INSTALL_TIMEOUT_MS` with examples for offline and slow-network environments. Made-with: Cursor * fix(lbug): move DuckDB extension install child into script Keep the bounded out-of-process INSTALL behavior, but replace the inline child code with a stable packaged ESM script. This makes the child process directly runnable and gives debuggable stack traces without source-vs-dist branching or a runtime transpiler. Made-with: Cursor
37 lines
1.2 KiB
JavaScript
37 lines
1.2 KiB
JavaScript
#!/usr/bin/env node
|
|
import fs from 'node:fs/promises';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import { createRequire } from 'node:module';
|
|
|
|
const EXTENSION_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
|
|
async function installDuckDbExtension(extensionName) {
|
|
if (!extensionName || !EXTENSION_NAME_PATTERN.test(extensionName)) {
|
|
throw new Error(`Invalid DuckDB extension name: ${extensionName ?? '<missing>'}`);
|
|
}
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const lbugModule = require('@ladybugdb/core');
|
|
const lbug = lbugModule.default ?? lbugModule;
|
|
|
|
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-ext-install-'));
|
|
const dbPath = path.join(tmpDir, 'install.lbug');
|
|
let db;
|
|
let conn;
|
|
|
|
try {
|
|
db = new lbug.Database(dbPath);
|
|
conn = new lbug.Connection(db);
|
|
await conn.query(`INSTALL ${extensionName}`);
|
|
} finally {
|
|
if (conn) await conn.close().catch(() => {});
|
|
if (db) await db.close().catch(() => {});
|
|
await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {});
|
|
}
|
|
}
|
|
|
|
installDuckDbExtension(process.argv[2] ?? process.env.GITNEXUS_LBUG_EXTENSION_NAME).catch((err) => {
|
|
console.error(err instanceof Error ? (err.stack ?? err.message) : String(err));
|
|
process.exitCode = 1;
|
|
});
|