mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-29 01:41:42 +00:00
Merge branch 'main' into fix/web-stop-nexus-ai-1615
This commit is contained in:
commit
46a15700b7
5 changed files with 167 additions and 12 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import { getRuntimeCapabilities, getRuntimeFingerprint } from '../core/platform/capabilities.js';
|
||||
import { resolveEmbeddingConfig } from '../core/embeddings/config.js';
|
||||
import { isHttpMode } from '../core/embeddings/http-client.js';
|
||||
import { checkLbugNative } from '../core/lbug/native-check.js';
|
||||
import { t } from './i18n/index.js';
|
||||
|
||||
function isCombiningMark(codePoint: number): boolean {
|
||||
|
|
@ -59,6 +60,13 @@ export const doctorCommand = async () => {
|
|||
console.log(` ${label('doctor.labels.node', 10)}${fingerprint.node}`);
|
||||
console.log(` ${label('doctor.labels.gitnexus', 10)}${fingerprint.gitnexus}`);
|
||||
console.log(` ${label('doctor.labels.ladybugdb', 10)}${fingerprint.ladybugdb ?? 'unknown'}`);
|
||||
const nativeCheck = checkLbugNative();
|
||||
if (nativeCheck.ok) {
|
||||
console.log(` ${padDisplayEnd('native', 10)}✓ lbugjs.node loaded`);
|
||||
} else {
|
||||
console.log(` ${padDisplayEnd('native', 10)}✗ lbugjs.node missing`);
|
||||
process.stderr.write(`\n${nativeCheck.message?.replace(/^/gm, ' ')}\n\n`);
|
||||
}
|
||||
console.log(` ${label('doctor.labels.onnx', 10)}${fingerprint.onnxruntime ?? 'unknown'}`);
|
||||
console.log('');
|
||||
console.log(t('doctor.capabilities'));
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { Command } from 'commander';
|
||||
import { createRequire } from 'node:module';
|
||||
import { createLazyAction } from './lazy-action.js';
|
||||
import { createLazyAction, createLbugLazyAction } from './lazy-action.js';
|
||||
import { registerGroupCommands } from './group.js';
|
||||
import { localizeCliHelp } from './help-i18n.js';
|
||||
import { t } from './i18n/index.js';
|
||||
|
|
@ -89,7 +89,7 @@ program
|
|||
.option('--embedding-sub-batch-size <n>', 'Number of chunks per embedding model call')
|
||||
.option('--embedding-device <device>', 'Embedding device: auto, cpu, dml, cuda, or wasm')
|
||||
.addHelpText('after', () => t('help.analyze.environment'))
|
||||
.action(createLazyAction(() => import('./analyze.js'), 'analyzeCommand'));
|
||||
.action(createLbugLazyAction(() => import('./analyze.js'), 'analyzeCommand'));
|
||||
|
||||
program
|
||||
.command('index [path...]')
|
||||
|
|
@ -105,12 +105,12 @@ program
|
|||
.description('Start local HTTP server for web UI connection')
|
||||
.option('-p, --port <port>', 'Port number', '4747')
|
||||
.option('--host <host>', 'Bind address (default: 127.0.0.1, use 0.0.0.0 for remote access)')
|
||||
.action(createLazyAction(() => import('./serve.js'), 'serveCommand'));
|
||||
.action(createLbugLazyAction(() => import('./serve.js'), 'serveCommand'));
|
||||
|
||||
program
|
||||
.command('mcp')
|
||||
.description('Start MCP server (stdio) — serves all indexed repos')
|
||||
.action(createLazyAction(() => import('./mcp.js'), 'mcpCommand'));
|
||||
.action(createLbugLazyAction(() => import('./mcp.js'), 'mcpCommand'));
|
||||
|
||||
program
|
||||
.command('list')
|
||||
|
|
@ -177,12 +177,12 @@ program
|
|||
'--lang <lang>',
|
||||
'Output language for generated documentation (e.g. english, chinese, spanish, japanese)',
|
||||
)
|
||||
.action(createLazyAction(() => import('./wiki.js'), 'wikiCommand'));
|
||||
.action(createLbugLazyAction(() => import('./wiki.js'), 'wikiCommand'));
|
||||
|
||||
program
|
||||
.command('augment <pattern>')
|
||||
.description('Augment a search pattern with knowledge graph context (used by hooks)')
|
||||
.action(createLazyAction(() => import('./augment.js'), 'augmentCommand'));
|
||||
.action(createLbugLazyAction(() => import('./augment.js'), 'augmentCommand'));
|
||||
|
||||
program
|
||||
.command('publish [path]')
|
||||
|
|
@ -207,7 +207,7 @@ program
|
|||
.option('-g, --goal <text>', 'What you want to find')
|
||||
.option('-l, --limit <n>', 'Max processes to return (default: 5)')
|
||||
.option('--content', 'Include full symbol source code')
|
||||
.action(createLazyAction(() => import('./tool.js'), 'queryCommand'));
|
||||
.action(createLbugLazyAction(() => import('./tool.js'), 'queryCommand'));
|
||||
|
||||
program
|
||||
.command('context [name]')
|
||||
|
|
@ -216,7 +216,7 @@ program
|
|||
.option('-u, --uid <uid>', 'Direct symbol UID (zero-ambiguity lookup)')
|
||||
.option('-f, --file <path>', 'File path to disambiguate common names')
|
||||
.option('--content', 'Include full symbol source code')
|
||||
.action(createLazyAction(() => import('./tool.js'), 'contextCommand'));
|
||||
.action(createLbugLazyAction(() => import('./tool.js'), 'contextCommand'));
|
||||
|
||||
program
|
||||
.command('impact <target>')
|
||||
|
|
@ -228,13 +228,13 @@ program
|
|||
.option('--limit <n>', 'Max symbols per depth level (default: 100)')
|
||||
.option('--offset <n>', 'Skip N symbols per depth level for pagination')
|
||||
.option('--summary-only', 'Return counts and risk only, omit symbol list')
|
||||
.action(createLazyAction(() => import('./tool.js'), 'impactCommand'));
|
||||
.action(createLbugLazyAction(() => import('./tool.js'), 'impactCommand'));
|
||||
|
||||
program
|
||||
.command('cypher <query>')
|
||||
.description('Execute raw Cypher query against the knowledge graph')
|
||||
.option('-r, --repo <name>', 'Target repository')
|
||||
.action(createLazyAction(() => import('./tool.js'), 'cypherCommand'));
|
||||
.action(createLbugLazyAction(() => import('./tool.js'), 'cypherCommand'));
|
||||
|
||||
program
|
||||
.command('detect-changes')
|
||||
|
|
@ -243,7 +243,7 @@ program
|
|||
.option('-s, --scope <scope>', 'What to analyze: unstaged, staged, all, or compare', 'unstaged')
|
||||
.option('-b, --base-ref <ref>', 'Branch/commit for compare scope (e.g. main)')
|
||||
.option('-r, --repo <name>', 'Target repository')
|
||||
.action(createLazyAction(() => import('./tool.js'), 'detectChangesCommand'));
|
||||
.action(createLbugLazyAction(() => import('./tool.js'), 'detectChangesCommand'));
|
||||
|
||||
// ─── Eval Server (persistent daemon for SWE-bench) ─────────────────
|
||||
|
||||
|
|
@ -256,7 +256,7 @@ program
|
|||
'Bind address (default: 127.0.0.1, use 0.0.0.0 to expose to all interfaces)',
|
||||
)
|
||||
.option('--idle-timeout <seconds>', 'Auto-shutdown after N seconds idle (0 = disabled)', '0')
|
||||
.action(createLazyAction(() => import('./eval-server.js'), 'evalServerCommand'));
|
||||
.action(createLbugLazyAction(() => import('./eval-server.js'), 'evalServerCommand'));
|
||||
|
||||
registerGroupCommands(program);
|
||||
localizeCliHelp(program);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
* at compile time — catching typos when used with concrete module imports.
|
||||
*/
|
||||
|
||||
import { checkLbugNative } from '../core/lbug/native-check.js';
|
||||
|
||||
function isCallable(value: unknown): value is (...args: unknown[]) => unknown {
|
||||
return typeof value === 'function';
|
||||
}
|
||||
|
|
@ -21,3 +23,23 @@ export function createLazyAction<
|
|||
await action(...args);
|
||||
};
|
||||
}
|
||||
|
||||
export function createLbugLazyAction<
|
||||
TModule extends Record<string, unknown>,
|
||||
TKey extends string & keyof TModule,
|
||||
>(loader: () => Promise<TModule>, exportName: TKey): (...args: unknown[]) => Promise<void> {
|
||||
return async (...args: unknown[]): Promise<void> => {
|
||||
const check = checkLbugNative();
|
||||
if (!check.ok) {
|
||||
process.stderr.write(`\n ${check.message?.replace(/\n/g, '\n ')}\n\n`);
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
const module = await loader();
|
||||
const action = module[exportName];
|
||||
if (!isCallable(action)) {
|
||||
throw new Error(`Lazy action export not found: ${exportName}`);
|
||||
}
|
||||
await action(...args);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
79
gitnexus/src/core/lbug/native-check.ts
Normal file
79
gitnexus/src/core/lbug/native-check.ts
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
export interface NativeCheckResult {
|
||||
ok: boolean;
|
||||
binaryPath?: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export function checkLbugNative(overridePkgDir?: string): NativeCheckResult {
|
||||
let pkgDir: string;
|
||||
|
||||
if (overridePkgDir) {
|
||||
pkgDir = overridePkgDir;
|
||||
} else {
|
||||
try {
|
||||
const _require = createRequire(import.meta.url);
|
||||
const mainEntry = _require.resolve('@ladybugdb/core');
|
||||
pkgDir = path.dirname(mainEntry);
|
||||
} catch {
|
||||
return {
|
||||
ok: false,
|
||||
message: [
|
||||
'LadybugDB package (@ladybugdb/core) is not installed.',
|
||||
'',
|
||||
'Run: npm install',
|
||||
].join('\n'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const binaryPath = path.join(pkgDir, 'lbugjs.node');
|
||||
if (!fs.existsSync(binaryPath)) {
|
||||
return {
|
||||
ok: false,
|
||||
binaryPath,
|
||||
message: [
|
||||
'LadybugDB native binary (lbugjs.node) is missing.',
|
||||
'',
|
||||
'This usually happens when the install lifecycle script was skipped.',
|
||||
'',
|
||||
'To repair:',
|
||||
` node ${path.join(pkgDir, 'install.js')}`,
|
||||
'',
|
||||
'If using bun, add to package.json and reinstall:',
|
||||
' "trustedDependencies": ["@ladybugdb/core"]',
|
||||
'',
|
||||
'Also check that npm is not configured with ignore-scripts=true',
|
||||
'(in .npmrc or via --ignore-scripts).',
|
||||
].join('\n'),
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const _require = createRequire(import.meta.url);
|
||||
_require(binaryPath);
|
||||
} catch (err: unknown) {
|
||||
const nativeError = err instanceof Error ? err.message : String(err);
|
||||
return {
|
||||
ok: false,
|
||||
binaryPath,
|
||||
message: [
|
||||
'LadybugDB native binary (lbugjs.node) exists but failed to load:',
|
||||
` ${nativeError}`,
|
||||
'',
|
||||
'This can happen with a truncated file, ABI mismatch, or wrong-platform binary.',
|
||||
'',
|
||||
'To repair:',
|
||||
` node ${path.join(pkgDir, 'install.js')}`,
|
||||
'',
|
||||
'If using bun, add to package.json and reinstall:',
|
||||
' "trustedDependencies": ["@ladybugdb/core"]',
|
||||
].join('\n'),
|
||||
};
|
||||
}
|
||||
|
||||
return { ok: true, binaryPath };
|
||||
}
|
||||
46
gitnexus/test/unit/lbug-native-check.test.ts
Normal file
46
gitnexus/test/unit/lbug-native-check.test.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import fs from 'fs/promises';
|
||||
import { checkLbugNative } from '../../src/core/lbug/native-check.js';
|
||||
|
||||
describe('checkLbugNative', () => {
|
||||
it('returns ok:true when the real @ladybugdb/core binary is present', () => {
|
||||
const result = checkLbugNative();
|
||||
expect(result.ok).toBe(true);
|
||||
expect(result.binaryPath).toBeDefined();
|
||||
expect(result.message).toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns ok:false with repair instructions when lbugjs.node is missing', async () => {
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'lbug-check-'));
|
||||
try {
|
||||
await fs.writeFile(path.join(tmpDir, 'install.js'), '');
|
||||
|
||||
const result = checkLbugNative(tmpDir);
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
expect(result.message).toContain('missing');
|
||||
expect(result.message).toContain('install.js');
|
||||
expect(result.message).toContain('trustedDependencies');
|
||||
expect(result.message).toContain('ignore-scripts');
|
||||
} finally {
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('returns ok:false when lbugjs.node exists but is unloadable (zero-byte)', async () => {
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'lbug-check-'));
|
||||
try {
|
||||
await fs.writeFile(path.join(tmpDir, 'lbugjs.node'), Buffer.alloc(0));
|
||||
|
||||
const result = checkLbugNative(tmpDir);
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
expect(result.message).toContain('failed to load');
|
||||
expect(result.message).toContain('install.js');
|
||||
} finally {
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue