codereview-aia 0.1.2 → 0.1.4

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.
Files changed (200) hide show
  1. package/dist/analysis/static/wpPhpcsRunner.d.ts +11 -0
  2. package/dist/analysis/static/wpPhpcsRunner.js +219 -0
  3. package/dist/analysis/static/wpPhpcsRunner.js.map +1 -0
  4. package/dist/clients/implementations/openRouterClient.js +2 -2
  5. package/dist/clients/implementations/openRouterClient.js.map +1 -1
  6. package/dist/clients/openRouterClient.js +2 -2
  7. package/dist/clients/openRouterClient.js.map +1 -1
  8. package/dist/clients/utils/promptFormatter.d.ts +3 -2
  9. package/dist/clients/utils/promptFormatter.js +82 -24
  10. package/dist/clients/utils/promptFormatter.js.map +1 -1
  11. package/dist/core/ConfigurationService.d.ts +21 -0
  12. package/dist/core/ConfigurationService.js +39 -0
  13. package/dist/core/ConfigurationService.js.map +1 -1
  14. package/dist/core/handlers/FileProcessingHandler.js +5 -0
  15. package/dist/core/handlers/FileProcessingHandler.js.map +1 -1
  16. package/dist/core/reviewOrchestrator.js +61 -1
  17. package/dist/core/reviewOrchestrator.js.map +1 -1
  18. package/dist/index.d.ts +0 -1
  19. package/dist/index.js +0 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/runtime/cliEntry.js +57 -4
  22. package/dist/runtime/cliEntry.js.map +1 -1
  23. package/dist/runtime/fileCollector.d.ts +10 -1
  24. package/dist/runtime/fileCollector.js +217 -2
  25. package/dist/runtime/fileCollector.js.map +1 -1
  26. package/dist/runtime/reporting/markdownReportBuilder.d.ts +2 -0
  27. package/dist/runtime/reporting/markdownReportBuilder.js +57 -0
  28. package/dist/runtime/reporting/markdownReportBuilder.js.map +1 -1
  29. package/dist/runtime/reviewPipeline.d.ts +22 -3
  30. package/dist/runtime/reviewPipeline.js +46 -7
  31. package/dist/runtime/reviewPipeline.js.map +1 -1
  32. package/dist/runtime/runAiCodeReview.d.ts +19 -1
  33. package/dist/runtime/runAiCodeReview.js +243 -8
  34. package/dist/runtime/runAiCodeReview.js.map +1 -1
  35. package/dist/runtime/ui/RuntimeApp.js +15 -4
  36. package/dist/runtime/ui/RuntimeApp.js.map +1 -1
  37. package/dist/runtime/ui/screens/ProgressScreen.d.ts +6 -1
  38. package/dist/runtime/ui/screens/ProgressScreen.js +28 -2
  39. package/dist/runtime/ui/screens/ProgressScreen.js.map +1 -1
  40. package/dist/runtime/ui/screens/ResultsScreen.js +8 -1
  41. package/dist/runtime/ui/screens/ResultsScreen.js.map +1 -1
  42. package/dist/types/review.d.ts +60 -0
  43. package/dist/utils/detection/frameworkDetector.js +55 -0
  44. package/dist/utils/detection/frameworkDetector.js.map +1 -1
  45. package/dist/utils/promptTemplateManager.js +1 -0
  46. package/dist/utils/promptTemplateManager.js.map +1 -1
  47. package/package.json +13 -10
  48. package/.cr-aia.yml +0 -23
  49. package/.crignore +0 -0
  50. package/src/analysis/FindingsExtractor.ts +0 -431
  51. package/src/analysis/ai-detection/analyzers/BaseAnalyzer.ts +0 -267
  52. package/src/analysis/ai-detection/analyzers/DocumentationAnalyzer.ts +0 -622
  53. package/src/analysis/ai-detection/analyzers/GitHistoryAnalyzer.ts +0 -430
  54. package/src/analysis/ai-detection/core/AIDetectionEngine.ts +0 -467
  55. package/src/analysis/ai-detection/types/DetectionTypes.ts +0 -406
  56. package/src/analysis/ai-detection/utils/SubmissionConverter.ts +0 -390
  57. package/src/analysis/context/ReviewContext.ts +0 -378
  58. package/src/analysis/context/index.ts +0 -7
  59. package/src/analysis/index.ts +0 -8
  60. package/src/analysis/tokens/TokenAnalysisFormatter.ts +0 -154
  61. package/src/analysis/tokens/TokenAnalyzer.ts +0 -747
  62. package/src/analysis/tokens/index.ts +0 -8
  63. package/src/clients/base/abstractClient.ts +0 -190
  64. package/src/clients/base/httpClient.ts +0 -160
  65. package/src/clients/base/index.ts +0 -12
  66. package/src/clients/base/modelDetection.ts +0 -107
  67. package/src/clients/base/responseProcessor.ts +0 -586
  68. package/src/clients/factory/clientFactory.ts +0 -55
  69. package/src/clients/factory/index.ts +0 -8
  70. package/src/clients/implementations/index.ts +0 -8
  71. package/src/clients/implementations/openRouterClient.ts +0 -411
  72. package/src/clients/openRouterClient.ts +0 -863
  73. package/src/clients/openRouterClientWrapper.ts +0 -44
  74. package/src/clients/utils/directoryStructure.ts +0 -52
  75. package/src/clients/utils/index.ts +0 -11
  76. package/src/clients/utils/languageDetection.ts +0 -44
  77. package/src/clients/utils/promptFormatter.ts +0 -105
  78. package/src/clients/utils/promptLoader.ts +0 -53
  79. package/src/clients/utils/tokenCounter.ts +0 -297
  80. package/src/core/ApiClientSelector.ts +0 -37
  81. package/src/core/ConfigurationService.ts +0 -591
  82. package/src/core/ConsolidationService.ts +0 -423
  83. package/src/core/InteractiveDisplayManager.ts +0 -81
  84. package/src/core/OutputManager.ts +0 -275
  85. package/src/core/ReviewGenerator.ts +0 -140
  86. package/src/core/fileDiscovery.ts +0 -237
  87. package/src/core/handlers/EstimationHandler.ts +0 -104
  88. package/src/core/handlers/FileProcessingHandler.ts +0 -204
  89. package/src/core/handlers/OutputHandler.ts +0 -125
  90. package/src/core/handlers/ReviewExecutor.ts +0 -104
  91. package/src/core/reviewOrchestrator.ts +0 -333
  92. package/src/core/utils/ModelInfoUtils.ts +0 -56
  93. package/src/formatters/outputFormatter.ts +0 -62
  94. package/src/formatters/utils/IssueFormatters.ts +0 -83
  95. package/src/formatters/utils/JsonFormatter.ts +0 -77
  96. package/src/formatters/utils/MarkdownFormatters.ts +0 -609
  97. package/src/formatters/utils/MetadataFormatter.ts +0 -269
  98. package/src/formatters/utils/ModelInfoExtractor.ts +0 -115
  99. package/src/index.ts +0 -28
  100. package/src/plugins/PluginInterface.ts +0 -50
  101. package/src/plugins/PluginManager.ts +0 -126
  102. package/src/prompts/PromptManager.ts +0 -69
  103. package/src/prompts/cache/PromptCache.ts +0 -50
  104. package/src/prompts/promptText/common/variables/css-frameworks.json +0 -33
  105. package/src/prompts/promptText/common/variables/framework-versions.json +0 -45
  106. package/src/prompts/promptText/frameworks/react/comprehensive.hbs +0 -19
  107. package/src/prompts/promptText/languages/css/comprehensive.hbs +0 -18
  108. package/src/prompts/promptText/languages/generic/comprehensive.hbs +0 -20
  109. package/src/prompts/promptText/languages/html/comprehensive.hbs +0 -18
  110. package/src/prompts/promptText/languages/javascript/comprehensive.hbs +0 -18
  111. package/src/prompts/promptText/languages/python/comprehensive.hbs +0 -18
  112. package/src/prompts/promptText/languages/typescript/comprehensive.hbs +0 -18
  113. package/src/runtime/auth/service.ts +0 -58
  114. package/src/runtime/auth/session.ts +0 -103
  115. package/src/runtime/auth/types.ts +0 -11
  116. package/src/runtime/cliEntry.ts +0 -196
  117. package/src/runtime/debug/logManager.ts +0 -37
  118. package/src/runtime/errors.ts +0 -13
  119. package/src/runtime/fileCollector.ts +0 -222
  120. package/src/runtime/manifest.ts +0 -64
  121. package/src/runtime/openrouterProxy.ts +0 -45
  122. package/src/runtime/preprod/webCheck.ts +0 -104
  123. package/src/runtime/proxyConfig.ts +0 -94
  124. package/src/runtime/proxyEnvironment.ts +0 -71
  125. package/src/runtime/reportMerge.ts +0 -102
  126. package/src/runtime/reporting/markdownReportBuilder.ts +0 -138
  127. package/src/runtime/reporting/reportDataCollector.ts +0 -234
  128. package/src/runtime/reporting/summaryGenerator.ts +0 -86
  129. package/src/runtime/reviewPipeline.ts +0 -161
  130. package/src/runtime/runAiCodeReview.ts +0 -153
  131. package/src/runtime/runtimeConfig.ts +0 -5
  132. package/src/runtime/ui/Layout.tsx +0 -57
  133. package/src/runtime/ui/RuntimeApp.tsx +0 -233
  134. package/src/runtime/ui/inkModules.ts +0 -73
  135. package/src/runtime/ui/screens/AuthScreen.tsx +0 -128
  136. package/src/runtime/ui/screens/ModeSelection.tsx +0 -185
  137. package/src/runtime/ui/screens/ProgressScreen.tsx +0 -62
  138. package/src/runtime/ui/screens/ResultsScreen.tsx +0 -83
  139. package/src/strategies/ArchitecturalReviewStrategy.ts +0 -54
  140. package/src/strategies/CodingTestReviewStrategy.ts +0 -920
  141. package/src/strategies/ConsolidatedReviewStrategy.ts +0 -59
  142. package/src/strategies/ExtractPatternsReviewStrategy.ts +0 -64
  143. package/src/strategies/MultiPassReviewStrategy.ts +0 -785
  144. package/src/strategies/ReviewStrategy.ts +0 -64
  145. package/src/strategies/StrategyFactory.ts +0 -79
  146. package/src/strategies/index.ts +0 -14
  147. package/src/tokenizers/baseTokenizer.ts +0 -61
  148. package/src/tokenizers/gptTokenizer.ts +0 -27
  149. package/src/tokenizers/index.ts +0 -8
  150. package/src/types/apiResponses.ts +0 -40
  151. package/src/types/cli.ts +0 -24
  152. package/src/types/common.ts +0 -39
  153. package/src/types/configuration.ts +0 -201
  154. package/src/types/handlebars.d.ts +0 -5
  155. package/src/types/patch.d.ts +0 -25
  156. package/src/types/review.ts +0 -294
  157. package/src/types/reviewContext.d.ts +0 -65
  158. package/src/types/reviewSchema.ts +0 -181
  159. package/src/types/structuredReview.ts +0 -167
  160. package/src/types/tokenAnalysis.ts +0 -56
  161. package/src/utils/FileReader.ts +0 -93
  162. package/src/utils/FileWriter.ts +0 -76
  163. package/src/utils/PathGenerator.ts +0 -97
  164. package/src/utils/api/apiUtils.ts +0 -14
  165. package/src/utils/api/index.ts +0 -1
  166. package/src/utils/apiErrorHandler.ts +0 -287
  167. package/src/utils/ciDataCollector.ts +0 -252
  168. package/src/utils/codingTestConfigLoader.ts +0 -466
  169. package/src/utils/dependencies/aiDependencyAnalyzer.ts +0 -454
  170. package/src/utils/detection/frameworkDetector.ts +0 -879
  171. package/src/utils/detection/index.ts +0 -10
  172. package/src/utils/detection/projectTypeDetector.ts +0 -518
  173. package/src/utils/diagramGenerator.ts +0 -206
  174. package/src/utils/errorLogger.ts +0 -60
  175. package/src/utils/estimationUtils.ts +0 -407
  176. package/src/utils/fileFilters.ts +0 -373
  177. package/src/utils/fileSystem.ts +0 -57
  178. package/src/utils/index.ts +0 -36
  179. package/src/utils/logger.ts +0 -290
  180. package/src/utils/pathValidator.ts +0 -98
  181. package/src/utils/priorityFilter.ts +0 -59
  182. package/src/utils/projectDocs.ts +0 -189
  183. package/src/utils/promptPaths.ts +0 -29
  184. package/src/utils/promptTemplateManager.ts +0 -157
  185. package/src/utils/review/consolidateReview.ts +0 -553
  186. package/src/utils/review/fixDisplay.ts +0 -100
  187. package/src/utils/review/fixImplementation.ts +0 -61
  188. package/src/utils/review/index.ts +0 -36
  189. package/src/utils/review/interactiveProcessing.ts +0 -294
  190. package/src/utils/review/progressTracker.ts +0 -296
  191. package/src/utils/review/reviewExtraction.ts +0 -382
  192. package/src/utils/review/types.ts +0 -46
  193. package/src/utils/reviewActionHandler.ts +0 -18
  194. package/src/utils/reviewParser.ts +0 -253
  195. package/src/utils/sanitizer.ts +0 -238
  196. package/src/utils/smartFileSelector.ts +0 -255
  197. package/src/utils/templateLoader.ts +0 -514
  198. package/src/utils/treeGenerator.ts +0 -153
  199. package/tsconfig.build.json +0 -14
  200. package/tsconfig.json +0 -59
@@ -1,13 +0,0 @@
1
- export class MissingCrIgnoreError extends Error {
2
- readonly workspaceRoot: string;
3
- readonly crIgnorePath: string;
4
-
5
- constructor(workspaceRoot: string, crIgnorePath: string) {
6
- super(
7
- `cr-aia needs a .crignore file in ${workspaceRoot}. Create one (can be empty, gitignore syntax) and rerun.`,
8
- );
9
- this.name = 'MissingCrIgnoreError';
10
- this.workspaceRoot = workspaceRoot;
11
- this.crIgnorePath = crIgnorePath;
12
- }
13
- }
@@ -1,222 +0,0 @@
1
- import { existsSync, statSync } from 'node:fs';
2
- import { relative as relativePath, resolve as resolvePath, isAbsolute as isAbsolutePath, join as joinPath } from 'node:path';
3
- import { execa } from 'execa';
4
- import { MissingCrIgnoreError } from './errors';
5
-
6
- export type FileCollectionMode = 'uncommitted' | 'preprod';
7
-
8
- export interface CollectFilesOptions {
9
- mode?: FileCollectionMode;
10
- }
11
-
12
- export async function collectFiles(options: CollectFilesOptions = {}): Promise<string[]> {
13
- try {
14
- const { stdout: rootOut } = await execa('git', ['rev-parse', '--show-toplevel']);
15
- const repoRoot = resolvePath(rootOut.trim());
16
- const workspaceRoot = resolvePath(process.cwd());
17
- const workspaceRelative = relativePath(repoRoot, workspaceRoot) || '.';
18
-
19
- if (workspaceRelative.startsWith('..')) {
20
- throw new Error('Workspace is outside of the current git repository.');
21
- }
22
-
23
- const crIgnorePath = joinPath(workspaceRoot, '.crignore');
24
- if (!existsSync(crIgnorePath)) {
25
- throw new MissingCrIgnoreError(workspaceRoot, crIgnorePath);
26
- }
27
-
28
- const isWithinWorkspace = (absolutePath: string): boolean => {
29
- if (workspaceRoot === repoRoot) {
30
- return true;
31
- }
32
-
33
- const rel = relativePath(workspaceRoot, absolutePath);
34
- if (rel === '') {
35
- return true;
36
- }
37
-
38
- return !rel.startsWith('..') && !isAbsolutePath(rel);
39
- };
40
-
41
- const normalize = (filePath: string | undefined | null): string | null => {
42
- if (!filePath) {
43
- return null;
44
- }
45
-
46
- if (filePath.startsWith('.git/') || filePath.includes('node_modules')) {
47
- return null;
48
- }
49
-
50
- const absolutePath = resolvePath(repoRoot, filePath);
51
- if (!isWithinWorkspace(absolutePath)) {
52
- return null;
53
- }
54
-
55
- if (!existsSync(absolutePath)) {
56
- return null;
57
- }
58
-
59
- const stats = statSync(absolutePath);
60
- if (!stats.isFile()) {
61
- return null;
62
- }
63
-
64
- return absolutePath;
65
- };
66
-
67
- const mode: FileCollectionMode = options.mode ?? 'uncommitted';
68
-
69
- const collectUncommittedFiles = async (): Promise<string[]> => {
70
- const gitWorkspaceOptions = { cwd: workspaceRoot } as const;
71
- const gitPathSpec = ['--', '.'] as const;
72
-
73
- const { stdout: statusOut } = await execa('git', ['status', '--porcelain=1', '-z', ...gitPathSpec], gitWorkspaceOptions);
74
- const { stdout: diffOut } = await execa('git', ['diff', '--name-only', '-z', ...gitPathSpec], gitWorkspaceOptions);
75
-
76
- const statusEntries = statusOut.split('\0').filter(Boolean);
77
- const statusFiles: string[] = [];
78
-
79
- for (let i = 0; i < statusEntries.length; i += 1) {
80
- const entry = statusEntries[i];
81
- const statusPrefix = entry.slice(0, 3);
82
- const pathPart = entry.slice(3).trim();
83
-
84
- if (!pathPart) {
85
- continue;
86
- }
87
-
88
- if (statusPrefix.trim().startsWith('R')) {
89
- const renamedTarget = statusEntries[i + 1];
90
- if (renamedTarget) {
91
- statusFiles.push(renamedTarget);
92
- i += 1;
93
- continue;
94
- }
95
- }
96
-
97
- statusFiles.push(pathPart);
98
- }
99
-
100
- const diffFiles = diffOut.split('\0').filter(Boolean);
101
- const unique = new Set([...statusFiles, ...diffFiles]);
102
- return Array.from(unique);
103
- };
104
-
105
- const collectWorkspaceFiles = async (): Promise<string[]> => {
106
- const pathSpec = workspaceRelative === '' ? '.' : workspaceRelative;
107
- const tracked = await listTrackedFiles(repoRoot, pathSpec || '.');
108
- const untracked = await listUntrackedFiles(repoRoot, pathSpec || '.');
109
- return Array.from(new Set([...tracked, ...untracked]));
110
- };
111
-
112
- const pendingFiles = mode === 'preprod' ? await collectWorkspaceFiles() : await collectUncommittedFiles();
113
-
114
- const resolved = pendingFiles
115
- .map((file) => normalize(file))
116
- .filter((file): file is string => Boolean(file));
117
-
118
- const gitFiltered = await excludeIgnored(repoRoot, resolved);
119
- return await excludeCrIgnored(workspaceRoot, crIgnorePath, gitFiltered);
120
- } catch (error: any) {
121
- if (error.message?.includes('not a git repository')) {
122
- throw new Error('Not a git repository. Please run this command from a git repository root.');
123
- }
124
- throw error;
125
- }
126
- }
127
-
128
- async function listTrackedFiles(repoRoot: string, pathSpec: string): Promise<string[]> {
129
- const { stdout } = await execa('git', ['ls-files', '-z', '--', pathSpec], {
130
- cwd: repoRoot,
131
- });
132
- return stdout.split('\0').filter(Boolean);
133
- }
134
-
135
- async function listUntrackedFiles(repoRoot: string, pathSpec: string): Promise<string[]> {
136
- const { stdout } = await execa('git', ['ls-files', '-z', '--others', '--exclude-standard', '--', pathSpec], {
137
- cwd: repoRoot,
138
- });
139
- return stdout.split('\0').filter(Boolean);
140
- }
141
-
142
- async function excludeIgnored(repoRoot: string, files: string[]): Promise<string[]> {
143
- if (files.length === 0) {
144
- return [];
145
- }
146
-
147
- const relPaths = files.map((file) => {
148
- const rel = relativePath(repoRoot, file);
149
- return rel.replace(/\\/g, '/');
150
- });
151
-
152
- const { stdout, exitCode } = await execa('git', ['check-ignore', '--stdin'], {
153
- cwd: repoRoot,
154
- input: relPaths.join('\n'),
155
- reject: false,
156
- });
157
-
158
- if (exitCode !== 0 && exitCode !== 1) {
159
- throw new Error(`git check-ignore failed with code ${exitCode}`);
160
- }
161
-
162
- if (!stdout) {
163
- return files;
164
- }
165
-
166
- const ignored = new Set(
167
- stdout
168
- .split('\n')
169
- .map((line) => line.trim())
170
- .filter(Boolean),
171
- );
172
-
173
- return files.filter((_, index) => !ignored.has(relPaths[index]));
174
- }
175
-
176
- async function excludeCrIgnored(
177
- workspaceRoot: string,
178
- ignoreFile: string,
179
- files: string[],
180
- ): Promise<string[]> {
181
- if (files.length === 0) {
182
- return [];
183
- }
184
-
185
- const entries = files
186
- .map((file) => {
187
- const rel = relativePath(workspaceRoot, file).replace(/\\/g, '/');
188
- return { file, rel };
189
- })
190
- .filter((entry) => entry.rel && entry.rel !== '.crignore');
191
-
192
- if (entries.length === 0) {
193
- return [];
194
- }
195
-
196
- const { stdout, exitCode } = await execa(
197
- 'git',
198
- ['-c', `core.excludesFile=${ignoreFile}`, 'check-ignore', '--stdin'],
199
- {
200
- cwd: workspaceRoot,
201
- input: entries.map((entry) => entry.rel).join('\n'),
202
- reject: false,
203
- },
204
- );
205
-
206
- if (exitCode !== 0 && exitCode !== 1) {
207
- throw new Error(`git check-ignore (crignore) failed with code ${exitCode}`);
208
- }
209
-
210
- if (!stdout) {
211
- return entries.map((entry) => entry.file);
212
- }
213
-
214
- const ignored = new Set(
215
- stdout
216
- .split('\n')
217
- .map((line) => line.trim())
218
- .filter(Boolean),
219
- );
220
-
221
- return entries.filter((entry) => !ignored.has(entry.rel)).map((entry) => entry.file);
222
- }
@@ -1,64 +0,0 @@
1
- import { existsSync, readFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import yaml from 'js-yaml';
4
-
5
- const DEFAULT_MANIFEST: Record<string, unknown> = {
6
- rules: {
7
- universal: {
8
- secret_leaks: 'high',
9
- todos_left: 'medium',
10
- dead_code: 'medium',
11
- license_headers: 'low',
12
- },
13
- ts: {
14
- no_any: 'high',
15
- prefer_readonly: 'medium',
16
- exhaustiveness_in_switch: 'high',
17
- },
18
- py: {
19
- type_hints_required: 'medium',
20
- no_bare_except: 'high',
21
- },
22
- go: {
23
- errcheck_required: 'high',
24
- context_passthrough: 'medium',
25
- },
26
- },
27
- policies: {
28
- fail_on: 'critical',
29
- },
30
- outputs: {
31
- report: 'json',
32
- },
33
- };
34
-
35
- export function loadManifest(repoRoot: string): Record<string, unknown> {
36
- const defaultManifest = clone(DEFAULT_MANIFEST);
37
-
38
- const overridePaths = [join(repoRoot, 'cr-manifest.yml'), join(repoRoot, '.cr-aia.yml')];
39
-
40
- for (const overridePath of overridePaths) {
41
- if (existsSync(overridePath)) {
42
- const override = yaml.load(readFileSync(overridePath, 'utf-8')) as Record<string, unknown>;
43
- return deepMerge(defaultManifest, override);
44
- }
45
- }
46
-
47
- return defaultManifest;
48
- }
49
-
50
- function deepMerge(defaultObj: Record<string, any>, overrideObj: Record<string, any>): Record<string, any> {
51
- const result: Record<string, any> = { ...defaultObj };
52
- for (const key of Object.keys(overrideObj)) {
53
- if (typeof overrideObj[key] === 'object' && overrideObj[key] !== null && !Array.isArray(overrideObj[key])) {
54
- result[key] = deepMerge((result[key] as Record<string, any>) || {}, overrideObj[key] as Record<string, any>);
55
- } else {
56
- result[key] = overrideObj[key];
57
- }
58
- }
59
- return result;
60
- }
61
-
62
- function clone<T>(value: T): T {
63
- return JSON.parse(JSON.stringify(value));
64
- }
@@ -1,45 +0,0 @@
1
- const DEFAULT_PROXY_BASE_URL = 'https://cr.ai.enki.si';
2
-
3
- export function getProxyBaseUrl(): string {
4
- return process.env.CR_AIA_PROXY_BASE_URL || DEFAULT_PROXY_BASE_URL;
5
- }
6
-
7
- export function resolveOpenRouterProxyUrl(): string {
8
- const base = getProxyBaseUrl().replace(/\/$/, '');
9
- if (base.endsWith('/openrouter-proxy')) {
10
- return base;
11
- }
12
- if (base.endsWith('/proxy')) {
13
- return `${base.replace(/\/proxy$/, '')}/openrouter-proxy`;
14
- }
15
- return `${base}/openrouter-proxy`;
16
- }
17
-
18
- export function buildOpenRouterProxyHeaders(): Record<string, string> {
19
- const headers: Record<string, string> = {
20
- 'Content-Type': 'application/json',
21
- };
22
-
23
- const sessionToken = process.env.CR_AIA_PROXY_SESSION_TOKEN;
24
- if (!sessionToken) {
25
- throw new Error(
26
- 'Missing session token. Run `node dist/index.js`, sign in, or set CR_AIA_PROXY_SESSION_TOKEN before running reviews.',
27
- );
28
- }
29
-
30
- headers.Authorization = `Bearer ${sessionToken}`;
31
- headers['x-session-token'] = sessionToken;
32
-
33
- return headers;
34
- }
35
-
36
- export function withProxyMetadata(payload: Record<string, unknown>): Record<string, unknown> {
37
- const enriched = { ...payload };
38
- if (process.env.CR_AIA_PROXY_HTTP_REFERER && enriched['http_referer'] === undefined) {
39
- enriched['http_referer'] = process.env.CR_AIA_PROXY_HTTP_REFERER;
40
- }
41
- if (enriched['x_title'] === undefined) {
42
- enriched['x_title'] = process.env.CR_AIA_PROXY_X_TITLE || 'cr-aia';
43
- }
44
- return enriched;
45
- }
@@ -1,104 +0,0 @@
1
- const DEFAULT_AIA_BASE_URL = 'https://ai.enki.si';
2
-
3
- function resolveAiaBaseUrl(): string {
4
- const configured = process.env.AIA_BASE_URL || process.env.CR_AIA_AIA_BASE_URL;
5
- const base = (configured || DEFAULT_AIA_BASE_URL).trim();
6
- return base.replace(/\/$/, '');
7
- }
8
-
9
- function summarizeErrorBody(body: string): string {
10
- if (!body.trim()) {
11
- return 'No response body';
12
- }
13
-
14
- if (/<html/i.test(body)) {
15
- const sanitized = body
16
- .replace(/<script[\s\S]*?<\/script>/gi, ' ')
17
- .replace(/<style[\s\S]*?<\/style>/gi, ' ')
18
- .replace(/<[^>]+>/g, ' ')
19
- .replace(/\s+/g, ' ')
20
- .trim();
21
- return sanitized ? `${sanitized.slice(0, 200)}${sanitized.length > 200 ? '…' : ''}` : 'HTML error response';
22
- }
23
-
24
- const normalized = body.replace(/\s+/g, ' ').trim();
25
- if (!normalized) {
26
- return 'Empty error body';
27
- }
28
- return normalized.length > 200 ? `${normalized.slice(0, 200)}…` : normalized;
29
- }
30
-
31
- class WebCheckRequestError extends Error {
32
- status?: number;
33
- detail?: string;
34
-
35
- constructor(message: string, options?: { status?: number; detail?: string; cause?: unknown }) {
36
- super(message, options?.cause ? { cause: options.cause } : undefined);
37
- this.name = 'WebCheckRequestError';
38
- this.status = options?.status;
39
- this.detail = options?.detail;
40
- }
41
- }
42
-
43
- async function extractErrorDetail(response: Response): Promise<string | undefined> {
44
- const contentType = response.headers.get('content-type') || '';
45
- if (contentType.includes('application/json')) {
46
- try {
47
- const payload = (await response.json()) as { error?: { message?: string }; message?: string };
48
- return payload?.error?.message || payload?.message || JSON.stringify(payload);
49
- } catch {
50
- return undefined;
51
- }
52
- }
53
-
54
- try {
55
- const textBody = await response.text();
56
- return summarizeErrorBody(textBody);
57
- } catch {
58
- return undefined;
59
- }
60
- }
61
-
62
- async function sendWebCheckRequest(endpoint: string, payload: Record<string, unknown>): Promise<void> {
63
- let response: Response;
64
- try {
65
- response = await fetch(endpoint, {
66
- method: 'POST',
67
- headers: {
68
- 'Content-Type': 'application/json',
69
- },
70
- body: JSON.stringify(payload),
71
- });
72
- } catch (networkError) {
73
- const message =
74
- networkError instanceof Error ? networkError.message : 'Website check request failed due to an unknown error';
75
- throw new WebCheckRequestError(`Website check request failed: ${message}`, { cause: networkError });
76
- }
77
-
78
- if (!response.ok) {
79
- const detail = await extractErrorDetail(response);
80
- const suffix = detail ? ` ${detail}` : '';
81
- throw new WebCheckRequestError(`Website check failed (${response.status}).${suffix}`, {
82
- status: response.status,
83
- detail,
84
- });
85
- }
86
- }
87
-
88
- export async function triggerManualWebCheck(targetUrl: string): Promise<void> {
89
- const baseUrl = resolveAiaBaseUrl();
90
- const manualEndpoint = `${baseUrl}/api/manual/check`;
91
- const legacyEndpoint = `${baseUrl}/api/check-website`;
92
- const missingEndpointStatuses = new Set([404, 405, 501]);
93
-
94
- try {
95
- await sendWebCheckRequest(manualEndpoint, { url: targetUrl });
96
- return;
97
- } catch (error) {
98
- if (!(error instanceof WebCheckRequestError) || !error.status || !missingEndpointStatuses.has(error.status)) {
99
- throw error;
100
- }
101
- }
102
-
103
- await sendWebCheckRequest(legacyEndpoint, { url: targetUrl, options: {} });
104
- }
@@ -1,94 +0,0 @@
1
- import { existsSync, readFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import yaml from 'js-yaml';
4
-
5
- export interface ProxySettings {
6
- baseUrl: string;
7
- httpReferer?: string;
8
- xTitle?: string;
9
- }
10
-
11
- const CR_AIA_CONFIG_CANDIDATES = ['.cr-aia.yml'];
12
- const DEFAULT_PROXY_BASE_URL = 'https://cr.ai.enki.si';
13
-
14
- export function loadProxySettings(repoRoot: string): ProxySettings {
15
- const config = loadCrAiaConfig(repoRoot);
16
- const scopedConfig = config && typeof config === 'object' ? getScopedConfig(config) : undefined;
17
-
18
- const envBase = sanitize(process.env.PROXY_BASE_URL);
19
- const fileBase = getFirstDefined<string>(config, scopedConfig, ['proxy_base_url', 'proxyBaseUrl']);
20
-
21
- const httpReferer =
22
- sanitize(process.env.PROXY_HTTP_REFERER) ||
23
- getFirstDefined<string>(config, scopedConfig, ['http_referer', 'httpReferer']);
24
-
25
- const xTitle =
26
- sanitize(process.env.PROXY_X_TITLE) ||
27
- getFirstDefined<string>(config, scopedConfig, ['x_title', 'xTitle']);
28
-
29
- const baseUrl = envBase || fileBase || DEFAULT_PROXY_BASE_URL;
30
-
31
- const settings: ProxySettings = {
32
- baseUrl,
33
- };
34
-
35
- if (httpReferer) settings.httpReferer = httpReferer;
36
- if (xTitle) settings.xTitle = xTitle;
37
- return settings;
38
- }
39
-
40
- function sanitize(value: string | undefined): string | undefined {
41
- return value && value.trim() ? value.trim() : undefined;
42
- }
43
-
44
- function loadCrAiaConfig(repoRoot: string): any | undefined {
45
- for (const candidate of CR_AIA_CONFIG_CANDIDATES) {
46
- const filePath = join(repoRoot, candidate);
47
- if (!existsSync(filePath)) continue;
48
-
49
- try {
50
- const raw = readFileSync(filePath, 'utf-8');
51
- if (candidate.endsWith('.json')) {
52
- return JSON.parse(raw);
53
- }
54
- return yaml.load(raw);
55
- } catch {
56
- return undefined;
57
- }
58
- }
59
- return undefined;
60
- }
61
-
62
- function getScopedConfig(config: Record<string, any>): Record<string, any> | undefined {
63
- if (!config) return undefined;
64
- if (typeof config.cr_aia === 'object') return config.cr_aia as Record<string, any>;
65
- if (typeof config.crAia === 'object') return config.crAia as Record<string, any>;
66
- if (typeof config['cr-aia'] === 'object') return config['cr-aia'] as Record<string, any>;
67
- return undefined;
68
- }
69
-
70
- function getFirstDefined<T>(
71
- rootConfig: Record<string, any> | undefined,
72
- scopedConfig: Record<string, any> | undefined,
73
- keys: string[],
74
- ): T | undefined {
75
- for (const key of keys) {
76
- const scopedValue = scopedConfig ? getNested(scopedConfig, key) : undefined;
77
- if (scopedValue !== undefined) return scopedValue as T;
78
-
79
- const rootValue = rootConfig ? getNested(rootConfig, key) : undefined;
80
- if (rootValue !== undefined) return rootValue as T;
81
- }
82
- return undefined;
83
- }
84
-
85
- function getNested(obj: Record<string, any>, pathKey: string): any {
86
- const segments = pathKey.split('.');
87
- let current: any = obj;
88
- for (const segment of segments) {
89
- if (!current || typeof current !== 'object') return undefined;
90
- if (!(segment in current)) return undefined;
91
- current = current[segment];
92
- }
93
- return current;
94
- }
@@ -1,71 +0,0 @@
1
- import type { ProxySettings } from './proxyConfig';
2
- import { loadProxySettings } from './proxyConfig';
3
- import { getSessionToken } from './auth/session';
4
-
5
- let cachedSettings: ProxySettings | null = null;
6
-
7
- function proxySuppliesApiKey(settings: ProxySettings): boolean {
8
- const override = process.env.CR_AIA_PROXY_SUPPLIES_API_KEY;
9
- if (override && override.trim()) {
10
- return override.trim().toLowerCase() !== 'false';
11
- }
12
- return settings.baseUrl.toLowerCase().includes('cr.ai.enki.si');
13
- }
14
-
15
- function ensureApiKeyPlaceholder(settings: ProxySettings): void {
16
- if (!proxySuppliesApiKey(settings)) {
17
- return;
18
- }
19
-
20
- const placeholder = process.env.CR_AIA_PROXY_API_KEY_PLACEHOLDER || 'provided-by-proxy';
21
-
22
- if (!process.env.AI_CODE_REVIEW_OPENROUTER_API_KEY) {
23
- process.env.AI_CODE_REVIEW_OPENROUTER_API_KEY = placeholder;
24
- }
25
-
26
- if (!process.env.OPENROUTER_API_KEY) {
27
- process.env.OPENROUTER_API_KEY = placeholder;
28
- }
29
- }
30
-
31
- function applyProxySettings(settings: ProxySettings): void {
32
- process.env.CR_AIA_PROXY_BASE_URL = settings.baseUrl;
33
-
34
- if (settings.httpReferer) {
35
- process.env.CR_AIA_PROXY_HTTP_REFERER = settings.httpReferer;
36
- } else {
37
- delete process.env.CR_AIA_PROXY_HTTP_REFERER;
38
- }
39
-
40
- if (settings.xTitle) {
41
- process.env.CR_AIA_PROXY_X_TITLE = settings.xTitle;
42
- } else {
43
- delete process.env.CR_AIA_PROXY_X_TITLE;
44
- }
45
-
46
- ensureApiKeyPlaceholder(settings);
47
- ensureSessionToken();
48
- }
49
-
50
- function ensureSessionToken(): void {
51
- if (process.env.CR_AIA_PROXY_SESSION_TOKEN && process.env.CR_AIA_PROXY_SESSION_TOKEN.trim()) {
52
- return;
53
- }
54
-
55
- const token = getSessionToken();
56
- if (token) {
57
- process.env.CR_AIA_PROXY_SESSION_TOKEN = token;
58
- }
59
- }
60
-
61
- export function ensureProxyEnvironmentInitialized(repoRoot: string): ProxySettings {
62
- if (!cachedSettings) {
63
- cachedSettings = loadProxySettings(repoRoot);
64
- applyProxySettings(cachedSettings);
65
- }
66
- return cachedSettings;
67
- }
68
-
69
- export function getActiveProxySettings(): ProxySettings | null {
70
- return cachedSettings;
71
- }