@code-rag/cli 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +27 -0
- package/dist/cli.test.d.ts +1 -0
- package/dist/cli.test.js +369 -0
- package/dist/cli.test.js.map +1 -0
- package/dist/commands/hooks-cmd.d.ts +53 -0
- package/dist/commands/hooks-cmd.js +279 -0
- package/dist/commands/index-cmd.d.ts +4 -0
- package/dist/commands/index-cmd.js +1037 -0
- package/dist/commands/index-cmd.js.map +1 -0
- package/dist/commands/index-cmd.test.d.ts +1 -0
- package/dist/commands/index-cmd.test.js +74 -0
- package/dist/commands/index-cmd.test.js.map +1 -0
- package/dist/commands/init-wizard.d.ts +95 -0
- package/dist/commands/init-wizard.js +526 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.js +125 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/search.d.ts +7 -0
- package/dist/commands/search.js +124 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/serve.d.ts +2 -0
- package/dist/commands/serve.js +56 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/commands/status.d.ts +21 -0
- package/dist/commands/status.js +117 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/viewer.d.ts +20 -0
- package/dist/commands/viewer.js +197 -0
- package/dist/commands/viewer.js.map +1 -0
- package/dist/commands/viewer.test.d.ts +1 -0
- package/dist/commands/viewer.test.js +69 -0
- package/dist/commands/viewer.test.js.map +1 -0
- package/dist/commands/watch-cmd.d.ts +8 -0
- package/dist/commands/watch-cmd.js +152 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { join, resolve, sep } from 'node:path';
|
|
4
|
+
import { loadConfig, OllamaEmbeddingProvider, LanceDBStore, BM25Index, HybridSearch, } from '@code-rag/core';
|
|
5
|
+
/**
|
|
6
|
+
* Format a single search result for terminal display.
|
|
7
|
+
*/
|
|
8
|
+
export function formatSearchResult(result, index) {
|
|
9
|
+
const lines = [];
|
|
10
|
+
const rank = chalk.dim(`[${index + 1}]`);
|
|
11
|
+
const score = chalk.green(result.score.toFixed(4));
|
|
12
|
+
const filePath = chalk.cyan(result.chunk?.filePath ?? result.metadata.name ?? 'unknown');
|
|
13
|
+
const chunkType = chalk.magenta(result.metadata.chunkType);
|
|
14
|
+
let lineRange = '';
|
|
15
|
+
if (result.chunk && result.chunk.startLine > 0) {
|
|
16
|
+
lineRange = chalk.dim(` L${result.chunk.startLine}-${result.chunk.endLine}`);
|
|
17
|
+
}
|
|
18
|
+
lines.push(`${rank} ${filePath}${lineRange} ${chunkType} score: ${score}`);
|
|
19
|
+
if (result.nlSummary) {
|
|
20
|
+
lines.push(` ${chalk.dim(result.nlSummary)}`);
|
|
21
|
+
}
|
|
22
|
+
return lines.join('\n');
|
|
23
|
+
}
|
|
24
|
+
export function registerSearchCommand(program) {
|
|
25
|
+
program
|
|
26
|
+
.command('search')
|
|
27
|
+
.description('Search the indexed codebase')
|
|
28
|
+
.argument('<query>', 'Search query')
|
|
29
|
+
.option('--language <lang>', 'Filter by programming language')
|
|
30
|
+
.option('--type <chunkType>', 'Filter by chunk type (function, class, method, etc.)')
|
|
31
|
+
.option('--file <path>', 'Filter by file path substring')
|
|
32
|
+
.option('--top-k <n>', 'Maximum number of results', '10')
|
|
33
|
+
.action(async (query, options) => {
|
|
34
|
+
try {
|
|
35
|
+
const rootDir = process.cwd();
|
|
36
|
+
const topK = parseInt(options.topK, 10);
|
|
37
|
+
if (isNaN(topK) || topK < 1) {
|
|
38
|
+
// eslint-disable-next-line no-console
|
|
39
|
+
console.error(chalk.red('Invalid --top-k value. Must be a positive integer.'));
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
// Load config
|
|
43
|
+
const configResult = await loadConfig(rootDir);
|
|
44
|
+
if (configResult.isErr()) {
|
|
45
|
+
// eslint-disable-next-line no-console
|
|
46
|
+
console.error(chalk.red('Config not found.'), 'Run "coderag init" first.');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
const config = configResult.value;
|
|
50
|
+
const storagePath = resolve(rootDir, config.storage.path);
|
|
51
|
+
// Prevent path traversal outside project root
|
|
52
|
+
if (!storagePath.startsWith(resolve(rootDir) + sep) && storagePath !== resolve(rootDir)) {
|
|
53
|
+
// eslint-disable-next-line no-console
|
|
54
|
+
console.error(chalk.red('Storage path escapes project root'));
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
// Create services
|
|
58
|
+
const embeddingProvider = new OllamaEmbeddingProvider({
|
|
59
|
+
model: config.embedding.model,
|
|
60
|
+
dimensions: config.embedding.dimensions,
|
|
61
|
+
});
|
|
62
|
+
const store = new LanceDBStore(storagePath, config.embedding.dimensions);
|
|
63
|
+
await store.connect();
|
|
64
|
+
// Load BM25 index
|
|
65
|
+
let bm25 = new BM25Index();
|
|
66
|
+
const bm25Path = join(storagePath, 'bm25-index.json');
|
|
67
|
+
try {
|
|
68
|
+
const bm25Data = await readFile(bm25Path, 'utf-8');
|
|
69
|
+
bm25 = BM25Index.deserialize(bm25Data);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// No BM25 index yet
|
|
73
|
+
}
|
|
74
|
+
// Create hybrid search
|
|
75
|
+
const hybridSearch = new HybridSearch(store, bm25, embeddingProvider, config.search);
|
|
76
|
+
// Run search
|
|
77
|
+
const searchResult = await hybridSearch.search(query, { topK });
|
|
78
|
+
if (searchResult.isErr()) {
|
|
79
|
+
store.close();
|
|
80
|
+
// eslint-disable-next-line no-console
|
|
81
|
+
console.error(chalk.red('Search failed:'), searchResult.error.message);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
let results = searchResult.value;
|
|
85
|
+
// Apply filters
|
|
86
|
+
if (options.language) {
|
|
87
|
+
const lang = options.language.toLowerCase();
|
|
88
|
+
results = results.filter((r) => r.chunk?.language?.toLowerCase() === lang);
|
|
89
|
+
}
|
|
90
|
+
if (options.type) {
|
|
91
|
+
const chunkType = options.type.toLowerCase();
|
|
92
|
+
results = results.filter((r) => r.metadata.chunkType.toLowerCase() === chunkType);
|
|
93
|
+
}
|
|
94
|
+
if (options.file) {
|
|
95
|
+
const fileFilter = options.file.toLowerCase();
|
|
96
|
+
results = results.filter((r) => (r.chunk?.filePath ?? '').toLowerCase().includes(fileFilter));
|
|
97
|
+
}
|
|
98
|
+
store.close();
|
|
99
|
+
// Display results
|
|
100
|
+
if (results.length === 0) {
|
|
101
|
+
// eslint-disable-next-line no-console
|
|
102
|
+
console.log(chalk.yellow('No results found.'));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
// eslint-disable-next-line no-console
|
|
106
|
+
console.log(chalk.bold(`Found ${results.length} result(s) for "${query}":\n`));
|
|
107
|
+
for (let i = 0; i < results.length; i++) {
|
|
108
|
+
const result = results[i];
|
|
109
|
+
// eslint-disable-next-line no-console
|
|
110
|
+
console.log(formatSearchResult(result, i));
|
|
111
|
+
if (i < results.length - 1) {
|
|
112
|
+
// eslint-disable-next-line no-console
|
|
113
|
+
console.log('');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
119
|
+
// eslint-disable-next-line no-console
|
|
120
|
+
console.error(chalk.red('Search failed:'), message);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/commands/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { CodeRAGServer, NO_INDEX_MESSAGE } from '@code-rag/mcp-server';
|
|
3
|
+
export function registerServeCommand(program) {
|
|
4
|
+
program
|
|
5
|
+
.command('serve')
|
|
6
|
+
.description('Start the CodeRAG MCP server')
|
|
7
|
+
.option('--port <port>', 'Port for SSE transport')
|
|
8
|
+
.action(async (options) => {
|
|
9
|
+
try {
|
|
10
|
+
const rootDir = process.cwd();
|
|
11
|
+
const server = new CodeRAGServer({ rootDir });
|
|
12
|
+
// Guard: check if index exists before starting the server
|
|
13
|
+
const indexCheck = await server.checkIndex();
|
|
14
|
+
if (indexCheck !== null && !indexCheck.exists) {
|
|
15
|
+
// eslint-disable-next-line no-console
|
|
16
|
+
console.error(chalk.yellow(NO_INDEX_MESSAGE));
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
await server.initialize();
|
|
20
|
+
// Graceful shutdown
|
|
21
|
+
const shutdown = () => {
|
|
22
|
+
// eslint-disable-next-line no-console
|
|
23
|
+
console.error(chalk.blue('[coderag]'), 'Shutting down...');
|
|
24
|
+
server.close().finally(() => process.exit(0));
|
|
25
|
+
};
|
|
26
|
+
process.on('SIGINT', shutdown);
|
|
27
|
+
process.on('SIGTERM', shutdown);
|
|
28
|
+
if (options.port) {
|
|
29
|
+
const port = parseInt(options.port, 10);
|
|
30
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
31
|
+
// eslint-disable-next-line no-console
|
|
32
|
+
console.error(chalk.red('[coderag] Invalid port number'));
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
// eslint-disable-next-line no-console
|
|
36
|
+
console.error(chalk.blue('[coderag]'), `Starting MCP server (SSE transport on port ${port})...`);
|
|
37
|
+
await server.connectSSE(port);
|
|
38
|
+
// eslint-disable-next-line no-console
|
|
39
|
+
console.error(chalk.green('[coderag]'), `MCP server running on http://localhost:${port}/sse`);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// eslint-disable-next-line no-console
|
|
43
|
+
console.error(chalk.blue('[coderag]'), 'Starting MCP server (stdio transport)...');
|
|
44
|
+
await server.connectStdio();
|
|
45
|
+
// eslint-disable-next-line no-console
|
|
46
|
+
console.error(chalk.green('[coderag]'), 'MCP server running on stdio');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
51
|
+
// eslint-disable-next-line no-console
|
|
52
|
+
console.error(chalk.red('[coderag] Server failed:'), message);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* Status information about the CodeRAG index.
|
|
4
|
+
*/
|
|
5
|
+
export interface StatusInfo {
|
|
6
|
+
totalChunks: number;
|
|
7
|
+
model: string;
|
|
8
|
+
dimensions: number;
|
|
9
|
+
languages: string[] | 'auto';
|
|
10
|
+
storagePath: string;
|
|
11
|
+
health: 'ok' | 'degraded' | 'not_initialized';
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Format status info for human-readable terminal output.
|
|
15
|
+
*/
|
|
16
|
+
export declare function formatStatus(status: StatusInfo): string;
|
|
17
|
+
/**
|
|
18
|
+
* Format status info as JSON.
|
|
19
|
+
*/
|
|
20
|
+
export declare function formatStatusJSON(status: StatusInfo): string;
|
|
21
|
+
export declare function registerStatusCommand(program: Command): void;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { resolve, sep } from 'node:path';
|
|
3
|
+
import { loadConfig, LanceDBStore, } from '@code-rag/core';
|
|
4
|
+
/**
|
|
5
|
+
* Format status info for human-readable terminal output.
|
|
6
|
+
*/
|
|
7
|
+
export function formatStatus(status) {
|
|
8
|
+
const lines = [];
|
|
9
|
+
lines.push(chalk.bold('CodeRAG Status'));
|
|
10
|
+
lines.push('');
|
|
11
|
+
const healthColor = status.health === 'ok'
|
|
12
|
+
? chalk.green
|
|
13
|
+
: status.health === 'degraded'
|
|
14
|
+
? chalk.yellow
|
|
15
|
+
: chalk.red;
|
|
16
|
+
lines.push(` Health: ${healthColor(status.health)}`);
|
|
17
|
+
lines.push(` Total chunks: ${chalk.cyan(String(status.totalChunks))}`);
|
|
18
|
+
lines.push(` Model: ${chalk.cyan(status.model)}`);
|
|
19
|
+
lines.push(` Dimensions: ${chalk.cyan(String(status.dimensions))}`);
|
|
20
|
+
const langDisplay = status.languages === 'auto'
|
|
21
|
+
? 'auto'
|
|
22
|
+
: status.languages.join(', ');
|
|
23
|
+
lines.push(` Languages: ${chalk.cyan(langDisplay)}`);
|
|
24
|
+
lines.push(` Storage: ${chalk.dim(status.storagePath)}`);
|
|
25
|
+
return lines.join('\n');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Format status info as JSON.
|
|
29
|
+
*/
|
|
30
|
+
export function formatStatusJSON(status) {
|
|
31
|
+
return JSON.stringify(status, null, 2);
|
|
32
|
+
}
|
|
33
|
+
export function registerStatusCommand(program) {
|
|
34
|
+
program
|
|
35
|
+
.command('status')
|
|
36
|
+
.description('Show the current CodeRAG index status')
|
|
37
|
+
.option('--json', 'Output in JSON format')
|
|
38
|
+
.action(async (options) => {
|
|
39
|
+
try {
|
|
40
|
+
const rootDir = process.cwd();
|
|
41
|
+
// Load config
|
|
42
|
+
const configResult = await loadConfig(rootDir);
|
|
43
|
+
if (configResult.isErr()) {
|
|
44
|
+
const status = {
|
|
45
|
+
totalChunks: 0,
|
|
46
|
+
model: 'unknown',
|
|
47
|
+
dimensions: 0,
|
|
48
|
+
languages: 'auto',
|
|
49
|
+
storagePath: '',
|
|
50
|
+
health: 'not_initialized',
|
|
51
|
+
};
|
|
52
|
+
if (options.json) {
|
|
53
|
+
// eslint-disable-next-line no-console
|
|
54
|
+
console.log(formatStatusJSON(status));
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
// eslint-disable-next-line no-console
|
|
58
|
+
console.log(formatStatus(status));
|
|
59
|
+
// eslint-disable-next-line no-console
|
|
60
|
+
console.log('');
|
|
61
|
+
// eslint-disable-next-line no-console
|
|
62
|
+
console.log(chalk.yellow('Run "coderag init" to initialize the project.'));
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const config = configResult.value;
|
|
67
|
+
const storagePath = resolve(rootDir, config.storage.path);
|
|
68
|
+
// Prevent path traversal outside project root
|
|
69
|
+
if (!storagePath.startsWith(resolve(rootDir) + sep) && storagePath !== resolve(rootDir)) {
|
|
70
|
+
// eslint-disable-next-line no-console
|
|
71
|
+
console.error(chalk.red('Storage path escapes project root'));
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
// Connect to LanceDB to get chunk count
|
|
75
|
+
let totalChunks = 0;
|
|
76
|
+
let health = 'not_initialized';
|
|
77
|
+
try {
|
|
78
|
+
const store = new LanceDBStore(storagePath, config.embedding.dimensions);
|
|
79
|
+
await store.connect();
|
|
80
|
+
const countResult = await store.count();
|
|
81
|
+
if (countResult.isOk()) {
|
|
82
|
+
totalChunks = countResult.value;
|
|
83
|
+
health = totalChunks > 0 ? 'ok' : 'degraded';
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
health = 'degraded';
|
|
87
|
+
}
|
|
88
|
+
store.close();
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
health = 'degraded';
|
|
92
|
+
}
|
|
93
|
+
const status = {
|
|
94
|
+
totalChunks,
|
|
95
|
+
model: config.embedding.model,
|
|
96
|
+
dimensions: config.embedding.dimensions,
|
|
97
|
+
languages: config.project.languages,
|
|
98
|
+
storagePath,
|
|
99
|
+
health,
|
|
100
|
+
};
|
|
101
|
+
if (options.json) {
|
|
102
|
+
// eslint-disable-next-line no-console
|
|
103
|
+
console.log(formatStatusJSON(status));
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
// eslint-disable-next-line no-console
|
|
107
|
+
console.log(formatStatus(status));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
112
|
+
// eslint-disable-next-line no-console
|
|
113
|
+
console.error(chalk.red('Status check failed:'), message);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
export interface ViewerOptions {
|
|
3
|
+
readonly port: number;
|
|
4
|
+
readonly open: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the viewer dist directory, trying multiple known locations.
|
|
8
|
+
* Returns the absolute path to the dist directory if found, or null.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveViewerDist(): string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Start the CodeRAG Viewer web interface.
|
|
13
|
+
*
|
|
14
|
+
* Creates an HTTP server that:
|
|
15
|
+
* - Serves static SPA files from the viewer dist directory
|
|
16
|
+
* - Proxies /api/* requests to the CodeRAG API server
|
|
17
|
+
* - Falls back to index.html for SPA client-side routing
|
|
18
|
+
*/
|
|
19
|
+
export declare function viewerCommand(options: ViewerOptions): Promise<void>;
|
|
20
|
+
export declare function registerViewerCommand(program: Command): void;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { existsSync, statSync } from 'node:fs';
|
|
3
|
+
import { resolve, dirname, join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { createServer } from 'node:http';
|
|
6
|
+
import { readFile } from 'node:fs/promises';
|
|
7
|
+
const MIME_TYPES = {
|
|
8
|
+
'.html': 'text/html; charset=utf-8',
|
|
9
|
+
'.js': 'application/javascript; charset=utf-8',
|
|
10
|
+
'.css': 'text/css; charset=utf-8',
|
|
11
|
+
'.json': 'application/json; charset=utf-8',
|
|
12
|
+
'.svg': 'image/svg+xml',
|
|
13
|
+
'.png': 'image/png',
|
|
14
|
+
'.ico': 'image/x-icon',
|
|
15
|
+
'.woff': 'font/woff',
|
|
16
|
+
'.woff2': 'font/woff2',
|
|
17
|
+
'.map': 'application/json',
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the viewer dist directory, trying multiple known locations.
|
|
21
|
+
* Returns the absolute path to the dist directory if found, or null.
|
|
22
|
+
*/
|
|
23
|
+
export function resolveViewerDist() {
|
|
24
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
const candidates = [
|
|
26
|
+
// Relative from compiled CLI dist → viewer dist
|
|
27
|
+
resolve(currentDir, '..', '..', '..', 'viewer', 'dist'),
|
|
28
|
+
// Relative from source → viewer dist
|
|
29
|
+
resolve(currentDir, '..', '..', 'viewer', 'dist'),
|
|
30
|
+
// Monorepo root → viewer dist
|
|
31
|
+
resolve(currentDir, '..', '..', '..', '..', 'packages', 'viewer', 'dist'),
|
|
32
|
+
];
|
|
33
|
+
for (const candidate of candidates) {
|
|
34
|
+
if (existsSync(candidate) && statSync(candidate).isDirectory()) {
|
|
35
|
+
return candidate;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Serve static files from the viewer dist directory.
|
|
42
|
+
*/
|
|
43
|
+
async function serveStatic(distPath, req, res) {
|
|
44
|
+
const url = req.url ?? '/';
|
|
45
|
+
const pathname = new URL(url, 'http://localhost').pathname;
|
|
46
|
+
// Map URL path to file path
|
|
47
|
+
let filePath;
|
|
48
|
+
if (pathname === '/' || pathname === '') {
|
|
49
|
+
filePath = join(distPath, 'index.html');
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
filePath = join(distPath, pathname);
|
|
53
|
+
}
|
|
54
|
+
// Security: prevent path traversal
|
|
55
|
+
const normalizedPath = resolve(filePath);
|
|
56
|
+
if (!normalizedPath.startsWith(resolve(distPath))) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
// Check file exists
|
|
60
|
+
if (!existsSync(normalizedPath) || !statSync(normalizedPath).isFile()) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const content = await readFile(normalizedPath);
|
|
65
|
+
const ext = normalizedPath.slice(normalizedPath.lastIndexOf('.'));
|
|
66
|
+
const mimeType = MIME_TYPES[ext] ?? 'application/octet-stream';
|
|
67
|
+
res.writeHead(200, {
|
|
68
|
+
'Content-Type': mimeType,
|
|
69
|
+
'Content-Length': content.length,
|
|
70
|
+
'Cache-Control': ext === '.html' ? 'no-cache' : 'public, max-age=31536000, immutable',
|
|
71
|
+
});
|
|
72
|
+
res.end(content);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Start the CodeRAG Viewer web interface.
|
|
81
|
+
*
|
|
82
|
+
* Creates an HTTP server that:
|
|
83
|
+
* - Serves static SPA files from the viewer dist directory
|
|
84
|
+
* - Proxies /api/* requests to the CodeRAG API server
|
|
85
|
+
* - Falls back to index.html for SPA client-side routing
|
|
86
|
+
*/
|
|
87
|
+
export async function viewerCommand(options) {
|
|
88
|
+
const distPath = resolveViewerDist();
|
|
89
|
+
if (!distPath) {
|
|
90
|
+
// eslint-disable-next-line no-console
|
|
91
|
+
console.error(chalk.red('[coderag]'), 'Viewer not built. Run', chalk.cyan('pnpm --filter @code-rag/viewer build'), 'first.');
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
const { port } = options;
|
|
95
|
+
let apiHandler = null;
|
|
96
|
+
try {
|
|
97
|
+
const { ApiServer } = await import('@code-rag/api-server');
|
|
98
|
+
const rootDir = process.cwd();
|
|
99
|
+
const apiServer = new ApiServer({ rootDir, port: port + 1 });
|
|
100
|
+
await apiServer.initialize();
|
|
101
|
+
// Express app is a callable (req, res) => void handler by design
|
|
102
|
+
const app = apiServer.getApp();
|
|
103
|
+
if (typeof app === 'function') {
|
|
104
|
+
apiHandler = app;
|
|
105
|
+
}
|
|
106
|
+
// eslint-disable-next-line no-console
|
|
107
|
+
console.error(chalk.blue('[coderag]'), 'API server initialized');
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// eslint-disable-next-line no-console
|
|
111
|
+
console.error(chalk.yellow('[coderag]'), 'API server not available. Viewer will serve static files only.');
|
|
112
|
+
}
|
|
113
|
+
const server = createServer(async (req, res) => {
|
|
114
|
+
const url = req.url ?? '/';
|
|
115
|
+
// Route /api/* to the API server if available
|
|
116
|
+
if (url.startsWith('/api/') || url === '/health') {
|
|
117
|
+
if (apiHandler) {
|
|
118
|
+
apiHandler(req, res);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
122
|
+
res.end(JSON.stringify({ error: 'API server not available' }));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// Try to serve static file
|
|
126
|
+
const served = await serveStatic(distPath, req, res);
|
|
127
|
+
if (served)
|
|
128
|
+
return;
|
|
129
|
+
// SPA fallback: serve index.html for client-side routing
|
|
130
|
+
const indexPath = join(distPath, 'index.html');
|
|
131
|
+
if (existsSync(indexPath)) {
|
|
132
|
+
try {
|
|
133
|
+
const content = await readFile(indexPath);
|
|
134
|
+
res.writeHead(200, {
|
|
135
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
136
|
+
'Content-Length': content.length,
|
|
137
|
+
'Cache-Control': 'no-cache',
|
|
138
|
+
});
|
|
139
|
+
res.end(content);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Fall through to 404
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
147
|
+
res.end('Not Found');
|
|
148
|
+
});
|
|
149
|
+
// Graceful shutdown
|
|
150
|
+
const shutdown = () => {
|
|
151
|
+
// eslint-disable-next-line no-console
|
|
152
|
+
console.error(chalk.blue('\n[coderag]'), 'Shutting down viewer...');
|
|
153
|
+
server.close(() => {
|
|
154
|
+
process.exit(0);
|
|
155
|
+
});
|
|
156
|
+
// Force exit after 5s if graceful close hangs
|
|
157
|
+
setTimeout(() => process.exit(0), 5000).unref();
|
|
158
|
+
};
|
|
159
|
+
process.on('SIGINT', shutdown);
|
|
160
|
+
process.on('SIGTERM', shutdown);
|
|
161
|
+
// Start listening
|
|
162
|
+
await new Promise((resolvePromise, reject) => {
|
|
163
|
+
server.on('error', reject);
|
|
164
|
+
server.listen(port, () => {
|
|
165
|
+
resolvePromise();
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
const url = `http://localhost:${port}`;
|
|
169
|
+
// eslint-disable-next-line no-console
|
|
170
|
+
console.error(chalk.green('[coderag]'), `Viewer running at ${chalk.cyan(url)}`);
|
|
171
|
+
// Open browser if requested
|
|
172
|
+
if (options.open) {
|
|
173
|
+
try {
|
|
174
|
+
const { exec } = await import('node:child_process');
|
|
175
|
+
const openCmd = process.platform === 'darwin' ? 'open' :
|
|
176
|
+
process.platform === 'win32' ? 'start' :
|
|
177
|
+
'xdg-open';
|
|
178
|
+
exec(`${openCmd} ${url}`);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// Silently ignore if browser cannot be opened
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
export function registerViewerCommand(program) {
|
|
186
|
+
program
|
|
187
|
+
.command('viewer')
|
|
188
|
+
.description('Launch the CodeRAG Viewer web interface')
|
|
189
|
+
.option('-p, --port <port>', 'Port number', '3333')
|
|
190
|
+
.option('--no-open', 'Do not open browser automatically')
|
|
191
|
+
.action(async (opts) => {
|
|
192
|
+
await viewerCommand({
|
|
193
|
+
port: parseInt(opts.port, 10),
|
|
194
|
+
open: opts.open !== false,
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewer.js","sourceRoot":"","sources":["../../src/commands/viewer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { registerViewerCommand, resolveViewerDist } from './viewer.js';
|
|
4
|
+
describe('registerViewerCommand', () => {
|
|
5
|
+
let program;
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
program = new Command();
|
|
8
|
+
program.name('coderag').version('0.1.0');
|
|
9
|
+
registerViewerCommand(program);
|
|
10
|
+
});
|
|
11
|
+
it('should register the viewer command', () => {
|
|
12
|
+
const commandNames = program.commands.map((cmd) => cmd.name());
|
|
13
|
+
expect(commandNames).toContain('viewer');
|
|
14
|
+
});
|
|
15
|
+
it('should have --port option with default 3333', () => {
|
|
16
|
+
const viewerCmd = program.commands.find((c) => c.name() === 'viewer');
|
|
17
|
+
expect(viewerCmd).toBeDefined();
|
|
18
|
+
const portOpt = viewerCmd.options.find((o) => o.long === '--port');
|
|
19
|
+
expect(portOpt).toBeDefined();
|
|
20
|
+
expect(portOpt.defaultValue).toBe('3333');
|
|
21
|
+
});
|
|
22
|
+
it('should have --no-open option', () => {
|
|
23
|
+
const viewerCmd = program.commands.find((c) => c.name() === 'viewer');
|
|
24
|
+
expect(viewerCmd).toBeDefined();
|
|
25
|
+
const opts = viewerCmd.options.map((o) => o.long);
|
|
26
|
+
expect(opts).toContain('--no-open');
|
|
27
|
+
});
|
|
28
|
+
it('should have a description', () => {
|
|
29
|
+
const viewerCmd = program.commands.find((c) => c.name() === 'viewer');
|
|
30
|
+
expect(viewerCmd).toBeDefined();
|
|
31
|
+
expect(viewerCmd.description()).toBeTruthy();
|
|
32
|
+
expect(viewerCmd.description()).toContain('Viewer');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe('resolveViewerDist', () => {
|
|
36
|
+
it('should return null when no dist directory exists', () => {
|
|
37
|
+
// resolveViewerDist looks relative to the compiled file location,
|
|
38
|
+
// which won't find a viewer/dist in test context
|
|
39
|
+
const result = resolveViewerDist();
|
|
40
|
+
// May or may not be null depending on if viewer is built,
|
|
41
|
+
// but the function should not throw
|
|
42
|
+
expect(result === null || typeof result === 'string').toBe(true);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe('CLI integration with viewer command', () => {
|
|
46
|
+
let program;
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
program = new Command();
|
|
49
|
+
program.name('coderag').version('0.1.0');
|
|
50
|
+
program.exitOverride();
|
|
51
|
+
registerViewerCommand(program);
|
|
52
|
+
});
|
|
53
|
+
it('should parse custom port', () => {
|
|
54
|
+
const viewerCmd = program.commands.find((c) => c.name() === 'viewer');
|
|
55
|
+
expect(viewerCmd).toBeDefined();
|
|
56
|
+
// Verify the port option short flag is -p
|
|
57
|
+
const portOpt = viewerCmd.options.find((o) => o.long === '--port');
|
|
58
|
+
expect(portOpt?.short).toBe('-p');
|
|
59
|
+
});
|
|
60
|
+
it('should register all expected commands including viewer', () => {
|
|
61
|
+
// Simulate what index.ts does
|
|
62
|
+
const fullProgram = new Command();
|
|
63
|
+
fullProgram.name('coderag').version('0.1.0');
|
|
64
|
+
registerViewerCommand(fullProgram);
|
|
65
|
+
expect(fullProgram.commands).toHaveLength(1);
|
|
66
|
+
expect(fullProgram.commands[0].name()).toBe('viewer');
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=viewer.test.js.map
|