codeflow-hook 2.0.1 → 2.0.3
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/bin/codeflow-hook.js +1 -1
- package/lib/cli-integration/src/index.ts +111 -6
- package/lib/cli-integration/src/simulationEngine.ts +118 -4
- package/package.json +1 -1
- package/lib/cli-integration/dist/index.d.ts +0 -128
- package/lib/cli-integration/dist/index.js +0 -585
- package/lib/cli-integration/dist/pipelineConfigs.d.ts +0 -60
- package/lib/cli-integration/dist/pipelineConfigs.js +0 -549
- package/lib/cli-integration/dist/simulationEngine.d.ts +0 -86
- package/lib/cli-integration/dist/simulationEngine.js +0 -475
- package/lib/cli-integration/dist/types.d.ts +0 -156
- package/lib/cli-integration/dist/types.js +0 -15
package/bin/codeflow-hook.js
CHANGED
|
@@ -21,9 +21,51 @@ import fs from 'fs';
|
|
|
21
21
|
// Load environment variables
|
|
22
22
|
config();
|
|
23
23
|
|
|
24
|
+
// Validate and sanitize environment variables
|
|
25
|
+
const validateEnvironmentVariables = () => {
|
|
26
|
+
const envVars = {
|
|
27
|
+
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
|
28
|
+
INGESTION_SERVICE_URL: process.env.INGESTION_SERVICE_URL || 'http://localhost:3000',
|
|
29
|
+
QUERY_SERVICE_URL: process.env.QUERY_SERVICE_URL || 'http://localhost:4000',
|
|
30
|
+
REQUEST_TIMEOUT: process.env.REQUEST_TIMEOUT || '30000',
|
|
31
|
+
REQUEST_RETRIES: process.env.REQUEST_RETRIES || '3'
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Validate log level
|
|
35
|
+
const validLogLevels = ['error', 'warn', 'info', 'debug'];
|
|
36
|
+
if (!validLogLevels.includes(envVars.LOG_LEVEL)) {
|
|
37
|
+
throw new Error(`Invalid LOG_LEVEL: ${envVars.LOG_LEVEL}. Must be one of: ${validLogLevels.join(', ')}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Validate URLs
|
|
41
|
+
if (!envVars.INGESTION_SERVICE_URL.startsWith('http://') && !envVars.INGESTION_SERVICE_URL.startsWith('https://')) {
|
|
42
|
+
throw new Error(`Invalid INGESTION_SERVICE_URL: ${envVars.INGESTION_SERVICE_URL}. Must start with http:// or https://`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!envVars.QUERY_SERVICE_URL.startsWith('http://') && !envVars.QUERY_SERVICE_URL.startsWith('https://')) {
|
|
46
|
+
throw new Error(`Invalid QUERY_SERVICE_URL: ${envVars.QUERY_SERVICE_URL}. Must start with http:// or https://`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Validate timeout
|
|
50
|
+
const timeout = parseInt(envVars.REQUEST_TIMEOUT, 10);
|
|
51
|
+
if (isNaN(timeout) || timeout <= 0 || timeout > 300000) { // Max 5 minutes
|
|
52
|
+
throw new Error(`Invalid REQUEST_TIMEOUT: ${envVars.REQUEST_TIMEOUT}. Must be between 1 and 300000 milliseconds`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Validate retries
|
|
56
|
+
const retries = parseInt(envVars.REQUEST_RETRIES, 10);
|
|
57
|
+
if (isNaN(retries) || retries <= 0 || retries > 10) {
|
|
58
|
+
throw new Error(`Invalid REQUEST_RETRIES: ${envVars.REQUEST_RETRIES}. Must be between 1 and 10`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return envVars;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const envVars = validateEnvironmentVariables();
|
|
65
|
+
|
|
24
66
|
// Configure logger
|
|
25
67
|
const logger = winston.createLogger({
|
|
26
|
-
level:
|
|
68
|
+
level: envVars.LOG_LEVEL,
|
|
27
69
|
format: winston.format.combine(
|
|
28
70
|
winston.format.timestamp(),
|
|
29
71
|
winston.format.errors({ stack: true }),
|
|
@@ -636,7 +678,7 @@ export class CLIIntegrationService {
|
|
|
636
678
|
}
|
|
637
679
|
|
|
638
680
|
/**
|
|
639
|
-
* Make HTTP request to backend service with retry logic
|
|
681
|
+
* Make HTTP request to backend service with retry logic and security validation
|
|
640
682
|
*/
|
|
641
683
|
private async makeBackendRequest(
|
|
642
684
|
url: string,
|
|
@@ -645,21 +687,32 @@ export class CLIIntegrationService {
|
|
|
645
687
|
): Promise<any> {
|
|
646
688
|
let lastError: any;
|
|
647
689
|
|
|
690
|
+
// Validate URL to prevent SSRF attacks
|
|
691
|
+
if (!this.isValidUrl(url)) {
|
|
692
|
+
throw new Error('Invalid URL provided');
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// Sanitize headers to prevent header injection
|
|
696
|
+
const sanitizedHeaders = this.sanitizeHeaders(headers);
|
|
697
|
+
|
|
648
698
|
for (let attempt = 1; attempt <= this.config.retries; attempt++) {
|
|
649
699
|
try {
|
|
650
700
|
const response = await axios.post(url, data, {
|
|
651
701
|
headers: {
|
|
652
|
-
...
|
|
653
|
-
'X-Attempt': attempt.toString()
|
|
702
|
+
...sanitizedHeaders,
|
|
703
|
+
'X-Attempt': attempt.toString(),
|
|
704
|
+
'User-Agent': 'Codeflow-CLI-Integration/1.0'
|
|
654
705
|
},
|
|
655
|
-
timeout: this.config.timeout
|
|
706
|
+
timeout: this.config.timeout,
|
|
707
|
+
maxRedirects: 3,
|
|
708
|
+
validateStatus: (status) => status < 500 // Don't throw on 4xx errors
|
|
656
709
|
});
|
|
657
710
|
|
|
658
711
|
return response;
|
|
659
712
|
} catch (error) {
|
|
660
713
|
lastError = error;
|
|
661
714
|
logger.warn(`Backend request attempt ${attempt} failed`, {
|
|
662
|
-
url,
|
|
715
|
+
url: this.sanitizeUrlForLogging(url),
|
|
663
716
|
attempt,
|
|
664
717
|
error: this.formatError(error)
|
|
665
718
|
});
|
|
@@ -674,6 +727,58 @@ export class CLIIntegrationService {
|
|
|
674
727
|
throw lastError;
|
|
675
728
|
}
|
|
676
729
|
|
|
730
|
+
/**
|
|
731
|
+
* Validate URL to prevent SSRF attacks
|
|
732
|
+
*/
|
|
733
|
+
private isValidUrl(url: string): boolean {
|
|
734
|
+
try {
|
|
735
|
+
const urlObj = new URL(url);
|
|
736
|
+
|
|
737
|
+
// Block internal network ranges
|
|
738
|
+
const hostname = urlObj.hostname;
|
|
739
|
+
if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname.startsWith('10.') ||
|
|
740
|
+
hostname.startsWith('192.168.') || hostname.match(/^172\.(1[6-9]|2\d|3[01])\./)) {
|
|
741
|
+
return false;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// Only allow HTTP/HTTPS protocols
|
|
745
|
+
return urlObj.protocol === 'http:' || urlObj.protocol === 'https:';
|
|
746
|
+
} catch {
|
|
747
|
+
return false;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Sanitize headers to prevent header injection
|
|
753
|
+
*/
|
|
754
|
+
private sanitizeHeaders(headers: Record<string, string>): Record<string, string> {
|
|
755
|
+
const sanitized: Record<string, string> = {};
|
|
756
|
+
|
|
757
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
758
|
+
// Remove potentially dangerous headers
|
|
759
|
+
if (key.toLowerCase().includes('cookie') || key.toLowerCase().includes('authorization')) {
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// Sanitize header values
|
|
764
|
+
sanitized[key] = value.replace(/[^\x20-\x7E]/g, '');
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
return sanitized;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Sanitize URL for logging to prevent log injection
|
|
772
|
+
*/
|
|
773
|
+
private sanitizeUrlForLogging(url: string): string {
|
|
774
|
+
try {
|
|
775
|
+
const urlObj = new URL(url);
|
|
776
|
+
return `${urlObj.protocol}//${urlObj.hostname}${urlObj.pathname}`;
|
|
777
|
+
} catch {
|
|
778
|
+
return url.replace(/[\r\n]/g, '');
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
677
782
|
/**
|
|
678
783
|
* Make GraphQL request to Query Service
|
|
679
784
|
*/
|
|
@@ -26,6 +26,10 @@ export class SimulationEngine {
|
|
|
26
26
|
const executionId = this.generateExecutionId();
|
|
27
27
|
const startTime = Date.now();
|
|
28
28
|
|
|
29
|
+
// Validate configuration before execution
|
|
30
|
+
this.validatePipelineConfig(config);
|
|
31
|
+
this.validateEnvironmentVariables();
|
|
32
|
+
|
|
29
33
|
this.context = {
|
|
30
34
|
pipelineId: config.id,
|
|
31
35
|
executionId,
|
|
@@ -105,6 +109,103 @@ export class SimulationEngine {
|
|
|
105
109
|
}
|
|
106
110
|
}
|
|
107
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Validate environment variables for security
|
|
114
|
+
*/
|
|
115
|
+
private validateEnvironmentVariables(): void {
|
|
116
|
+
// Validate environment variables that might affect simulation
|
|
117
|
+
const envVars = {
|
|
118
|
+
NODE_ENV: process.env.NODE_ENV || 'development',
|
|
119
|
+
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
|
120
|
+
SIMULATION_TIMEOUT: process.env.SIMULATION_TIMEOUT || '300000', // 5 minutes default
|
|
121
|
+
MAX_CONCURRENCY: process.env.MAX_CONCURRENCY || '5'
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Validate NODE_ENV
|
|
125
|
+
const validNodeEnvs = ['development', 'test', 'production'];
|
|
126
|
+
if (!validNodeEnvs.includes(envVars.NODE_ENV)) {
|
|
127
|
+
throw new Error(`Invalid NODE_ENV: ${envVars.NODE_ENV}. Must be one of: ${validNodeEnvs.join(', ')}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Validate LOG_LEVEL
|
|
131
|
+
const validLogLevels = ['error', 'warn', 'info', 'debug'];
|
|
132
|
+
if (!validLogLevels.includes(envVars.LOG_LEVEL)) {
|
|
133
|
+
throw new Error(`Invalid LOG_LEVEL: ${envVars.LOG_LEVEL}. Must be one of: ${validLogLevels.join(', ')}`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Validate simulation timeout
|
|
137
|
+
const timeout = parseInt(envVars.SIMULATION_TIMEOUT, 10);
|
|
138
|
+
if (isNaN(timeout) || timeout <= 0 || timeout > 3600000) { // Max 1 hour
|
|
139
|
+
throw new Error(`Invalid SIMULATION_TIMEOUT: ${envVars.SIMULATION_TIMEOUT}. Must be between 1 and 3600000 milliseconds`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Validate max concurrency
|
|
143
|
+
const concurrency = parseInt(envVars.MAX_CONCURRENCY, 10);
|
|
144
|
+
if (isNaN(concurrency) || concurrency <= 0 || concurrency > 20) {
|
|
145
|
+
throw new Error(`Invalid MAX_CONCURRENCY: ${envVars.MAX_CONCURRENCY}. Must be between 1 and 20`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Validate pipeline configuration for security and correctness
|
|
151
|
+
*/
|
|
152
|
+
private validatePipelineConfig(config: PipelineConfig): void {
|
|
153
|
+
// Validate pipeline ID format
|
|
154
|
+
if (!config.id || !/^[a-z0-9-]+$/.test(config.id)) {
|
|
155
|
+
throw new Error('Invalid pipeline ID format - must contain only lowercase letters, numbers, and hyphens');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Validate stage configurations
|
|
159
|
+
if (!config.stages || config.stages.length === 0) {
|
|
160
|
+
throw new Error('Pipeline must contain at least one stage');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Validate each stage
|
|
164
|
+
const stageIds = new Set<string>();
|
|
165
|
+
for (const stage of config.stages) {
|
|
166
|
+
// Check for duplicate stage IDs
|
|
167
|
+
if (stageIds.has(stage.id)) {
|
|
168
|
+
throw new Error(`Duplicate stage ID found: ${stage.id}`);
|
|
169
|
+
}
|
|
170
|
+
stageIds.add(stage.id);
|
|
171
|
+
|
|
172
|
+
// Validate stage ID format
|
|
173
|
+
if (!/^[a-z0-9-]+$/.test(stage.id)) {
|
|
174
|
+
throw new Error(`Invalid stage ID format: ${stage.id}`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Validate dependencies don't create cycles (basic check)
|
|
178
|
+
if (stage.dependencies.includes(stage.id)) {
|
|
179
|
+
throw new Error(`Stage ${stage.id} cannot depend on itself`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Validate timeout values
|
|
183
|
+
if (stage.timeout <= 0 || stage.timeout > 7200000) { // Max 2 hours
|
|
184
|
+
throw new Error(`Invalid timeout for stage ${stage.id}: must be between 1ms and 2 hours`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Validate success rate
|
|
188
|
+
if (stage.successRate < 0 || stage.successRate > 1) {
|
|
189
|
+
throw new Error(`Invalid success rate for stage ${stage.id}: must be between 0 and 1`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Validate duration range
|
|
193
|
+
if (stage.durationRange.min <= 0 || stage.durationRange.max <= 0 ||
|
|
194
|
+
stage.durationRange.min > stage.durationRange.max) {
|
|
195
|
+
throw new Error(`Invalid duration range for stage ${stage.id}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Validate pipeline settings
|
|
200
|
+
if (config.settings.maxConcurrency <= 0 || config.settings.maxConcurrency > 10) {
|
|
201
|
+
throw new Error('Invalid maxConcurrency setting: must be between 1 and 10');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (config.settings.timeout <= 0 || config.settings.timeout > 86400000) { // Max 24 hours
|
|
205
|
+
throw new Error('Invalid pipeline timeout: must be between 1ms and 24 hours');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
108
209
|
/**
|
|
109
210
|
* Execute stages respecting dependencies and concurrency limits
|
|
110
211
|
*/
|
|
@@ -381,18 +482,23 @@ export class SimulationEngine {
|
|
|
381
482
|
): { success: boolean; logs: string[]; metrics: StageMetrics; errors?: ErrorInfo[] } {
|
|
382
483
|
const logs: string[] = [];
|
|
383
484
|
|
|
384
|
-
|
|
385
|
-
|
|
485
|
+
// Sanitize input to prevent command injection
|
|
486
|
+
const imageName = this.sanitizeInput(stageConfig.config.imageName || 'app:latest');
|
|
487
|
+
const baseImage = this.sanitizeInput(stageConfig.config.baseImage || 'node:18-alpine');
|
|
488
|
+
|
|
489
|
+
logs.push(`[${new Date().toISOString()}] 🐳 Building Docker image \`${imageName}\``);
|
|
490
|
+
logs.push(`[${new Date().toISOString()}] 📦 Step 1/8 : FROM ${baseImage}`);
|
|
386
491
|
|
|
387
492
|
if (success) {
|
|
388
493
|
logs.push(`[${new Date().toISOString()}] 📦 Step 8/8 : CMD ["npm", "start"]`);
|
|
389
494
|
logs.push(`[${new Date().toISOString()}] ✅ Successfully built ${stageConfig.config.imageId || 'a1b2c3d4e5f6'}`);
|
|
390
495
|
logs.push(`[${new Date().toISOString()}] 🔍 Image size: ${50 + Math.random() * 200}MB`);
|
|
391
496
|
|
|
392
|
-
// Create artifact
|
|
497
|
+
// Create artifact with sanitized data
|
|
393
498
|
if (this.context) {
|
|
499
|
+
const sanitizedImageName = this.sanitizeInput(stageConfig.config.imageName || 'app');
|
|
394
500
|
this.context.artifacts.set(`${stageConfig.id}-image`, {
|
|
395
|
-
name: `${
|
|
501
|
+
name: `${sanitizedImageName}.tar.gz`,
|
|
396
502
|
type: 'docker-image',
|
|
397
503
|
size: Math.round(50 + Math.random() * 200) * 1024 * 1024,
|
|
398
504
|
path: `/artifacts/${stageConfig.id}`,
|
|
@@ -406,6 +512,14 @@ export class SimulationEngine {
|
|
|
406
512
|
return { success, logs, metrics };
|
|
407
513
|
}
|
|
408
514
|
|
|
515
|
+
/**
|
|
516
|
+
* Sanitize input to prevent injection attacks
|
|
517
|
+
*/
|
|
518
|
+
private sanitizeInput(input: string): string {
|
|
519
|
+
// Remove potentially dangerous characters
|
|
520
|
+
return input.replace(/[^\w\-\.\/:]/g, '');
|
|
521
|
+
}
|
|
522
|
+
|
|
409
523
|
/**
|
|
410
524
|
* Simulate unit test stage
|
|
411
525
|
*/
|
package/package.json
CHANGED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* CLI Integration Service - Phase 4
|
|
4
|
-
*
|
|
5
|
-
* Bridges the local CLI commands with EKG backend services.
|
|
6
|
-
* Transforms CLI operations from local processing to backend-driven workflows.
|
|
7
|
-
*
|
|
8
|
-
* Key transformations:
|
|
9
|
-
* - `codeflow index` → EKG Ingestion Service webhook simulation
|
|
10
|
-
* - `codeflow analyze-diff` → EKG Query Service context-enhanced analysis
|
|
11
|
-
*/
|
|
12
|
-
/**
|
|
13
|
-
* CLI Integration Service
|
|
14
|
-
* Provides methods that CLI commands can call to interact with EKG backend
|
|
15
|
-
*/
|
|
16
|
-
export declare class CLIIntegrationService {
|
|
17
|
-
private config;
|
|
18
|
-
private git;
|
|
19
|
-
constructor();
|
|
20
|
-
/**
|
|
21
|
-
* Index repository for EKG - equivalent to `codeflow index`
|
|
22
|
-
*
|
|
23
|
-
* Sends repository URL to EKG Ingestion Service for analysis and graph population
|
|
24
|
-
*/
|
|
25
|
-
indexRepository(options?: {
|
|
26
|
-
repositoryUrl?: string;
|
|
27
|
-
dryRun?: boolean;
|
|
28
|
-
}): Promise<{
|
|
29
|
-
success: boolean;
|
|
30
|
-
repositoryId?: string;
|
|
31
|
-
message: string;
|
|
32
|
-
stats?: {
|
|
33
|
-
indexedFiles: number;
|
|
34
|
-
analysisTime: number;
|
|
35
|
-
webhookAccepted: boolean;
|
|
36
|
-
};
|
|
37
|
-
}>;
|
|
38
|
-
/**
|
|
39
|
-
* Analyze code diff with EKG context enhancement
|
|
40
|
-
*
|
|
41
|
-
* Sends diff to Query Service for EKG-enhanced analysis instead of local RAG
|
|
42
|
-
*/
|
|
43
|
-
analyzeDiff(diffContent: string, options?: {
|
|
44
|
-
legacy?: boolean;
|
|
45
|
-
outputFormat?: 'console' | 'json';
|
|
46
|
-
}): Promise<{
|
|
47
|
-
success: boolean;
|
|
48
|
-
analysis: any;
|
|
49
|
-
message: string;
|
|
50
|
-
stats?: {
|
|
51
|
-
ekg_queries: number;
|
|
52
|
-
similar_repos_found: number;
|
|
53
|
-
analysis_time: number;
|
|
54
|
-
};
|
|
55
|
-
}>;
|
|
56
|
-
/**
|
|
57
|
-
* Analyze diff content and extract structured information
|
|
58
|
-
*/
|
|
59
|
-
private analyzeDiffContent;
|
|
60
|
-
/**
|
|
61
|
-
* Query EKG for context on affected files
|
|
62
|
-
*/
|
|
63
|
-
private getEKGContext;
|
|
64
|
-
/**
|
|
65
|
-
* Generate enhanced analysis using EKG context
|
|
66
|
-
*/
|
|
67
|
-
private generateEKGEnhancedAnalysis;
|
|
68
|
-
/**
|
|
69
|
-
* Get current repository information
|
|
70
|
-
*/
|
|
71
|
-
private getRepositoryInfo;
|
|
72
|
-
/**
|
|
73
|
-
* Get list of files that would be indexed
|
|
74
|
-
*/
|
|
75
|
-
private getIndexableFiles;
|
|
76
|
-
/**
|
|
77
|
-
* Make HTTP request to backend service with retry logic
|
|
78
|
-
*/
|
|
79
|
-
private makeBackendRequest;
|
|
80
|
-
/**
|
|
81
|
-
* Make GraphQL request to Query Service
|
|
82
|
-
*/
|
|
83
|
-
private makeGraphQLRequest;
|
|
84
|
-
/**
|
|
85
|
-
* Generate repository ID (similar to ingestion service)
|
|
86
|
-
*/
|
|
87
|
-
private generateRepositoryId;
|
|
88
|
-
/**
|
|
89
|
-
* Get current user information
|
|
90
|
-
*/
|
|
91
|
-
private getCurrentUser;
|
|
92
|
-
/**
|
|
93
|
-
* Detect language from file extension
|
|
94
|
-
*/
|
|
95
|
-
private detectLanguage;
|
|
96
|
-
/**
|
|
97
|
-
* Format error for logging and display
|
|
98
|
-
*/
|
|
99
|
-
private formatError;
|
|
100
|
-
}
|
|
101
|
-
export declare const cliIntegrationService: CLIIntegrationService;
|
|
102
|
-
export declare const indexProject: (options?: {
|
|
103
|
-
repositoryUrl?: string;
|
|
104
|
-
dryRun?: boolean;
|
|
105
|
-
}) => Promise<{
|
|
106
|
-
success: boolean;
|
|
107
|
-
repositoryId?: string;
|
|
108
|
-
message: string;
|
|
109
|
-
stats?: {
|
|
110
|
-
indexedFiles: number;
|
|
111
|
-
analysisTime: number;
|
|
112
|
-
webhookAccepted: boolean;
|
|
113
|
-
};
|
|
114
|
-
}>;
|
|
115
|
-
export declare const analyzeDiff: (diffContent: string, options?: {
|
|
116
|
-
legacy?: boolean;
|
|
117
|
-
outputFormat?: 'console' | 'json';
|
|
118
|
-
}) => Promise<{
|
|
119
|
-
success: boolean;
|
|
120
|
-
analysis: any;
|
|
121
|
-
message: string;
|
|
122
|
-
stats?: {
|
|
123
|
-
ekg_queries: number;
|
|
124
|
-
similar_repos_found: number;
|
|
125
|
-
analysis_time: number;
|
|
126
|
-
};
|
|
127
|
-
}>;
|
|
128
|
-
export default cliIntegrationService;
|