x-fidelity 1.5.1 → 1.6.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/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # [1.6.0](https://github.com/zotoio/x-fidelity/compare/v1.5.1...v1.6.0) (2024-07-27)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **telemetry:** option silent ([e72b366](https://github.com/zotoio/x-fidelity/commit/e72b36603d4a682fee993a330393f453926a187a))
7
+
8
+
9
+ ### Features
10
+
11
+ * **config:** local filesystem config, openai option, telemetry option ([16f481c](https://github.com/zotoio/x-fidelity/commit/16f481cbb5c5e2fefd1b7cfcc6e9371461609f85))
12
+
1
13
  ## [1.5.1](https://github.com/zotoio/x-fidelity/compare/v1.5.0...v1.5.1) (2024-07-25)
2
14
 
3
15
 
package/README.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  x-fidelity is an advanced CLI tool designed to enforce opinionated framework adherence checks within a codebase. It provides a flexible and extensible way to ensure your projects follow specific standards and best practices.
4
4
 
5
+ ```
6
+ =====================================
7
+ __ __ ________ ______
8
+ | ## | ## | ######## \######
9
+ \##\/ ## ______ | ##__ | ##
10
+ >## ## | \| ## \ | ##
11
+ / ####\ \######| ###### | ##
12
+ | ## \##\ | ## _| ##_
13
+ | ## | ## | ## | ## \
14
+ \## \## \## \######
15
+
16
+ -------------------------------------
17
+ ```
18
+
5
19
  ## Quick Start
6
20
 
7
21
  1. Install x-fidelity:
@@ -20,20 +34,6 @@ x-fidelity is an advanced CLI tool designed to enforce opinionated framework adh
20
34
  xfidelity --help
21
35
  ```
22
36
 
23
- ```
24
- =====================================
25
- __ __ ________ ______
26
- | ## | ## | ######## \######
27
- \##\/ ## ______ | ##__ | ##
28
- >## ## | \| ## \ | ##
29
- / ####\ \######| ###### | ##
30
- | ## \##\ | ## _| ##_
31
- | ## | ## | ## | ## \
32
- \## \## \## \######
33
-
34
- -------------------------------------
35
- ```
36
-
37
37
  ## Table of Contents
38
38
 
39
39
  1. [Intent and Purpose](#intent-and-purpose)
@@ -97,14 +97,17 @@ xfidelity
97
97
  Use command-line options for more control:
98
98
 
99
99
  ```sh
100
- xfidelity [-d --dir <directory>] [-c --configServer <url>] [-a --archetype <archetype>] [-m --mode <mode>] [-p --port <port>]
100
+ xfidelity [-d --dir <directory>] [-c --configServer <url>] [-a --archetype <archetype>] [-m --mode <cli|server>] [-p --port <port>] [-o --openaiEnabled <boolean>] [-t --telemetryCollector <url>] [-l --localConfig <path>]
101
101
  ```
102
102
 
103
103
  - `-d --dir <directory>`: Specify the root directory to analyze (default: current directory)
104
- - `-c --configServer <url>`: URL to fetch the configuration from
104
+ - `-c --configServer <url>`: URL to fetch the configuration from. eg. https://localhost:8888
105
105
  - `-a --archetype <archetype>`: Archetype to use for analysis (default: 'node-fullstack')
106
106
  - `-m --mode <mode>`: Run mode: 'cli' or 'server' (default: 'cli')
107
107
  - `-p --port <port>`: Port number for server mode (default: 8888)
108
+ - `-o --openaiEnabled <boolean>`: Enable OpenAI analysis (default: false)
109
+ - `-t --telemetryCollector <url>`: The URL telemetry data will be sent to for usage analysis
110
+ - `-l --localConfig <path>`: Path to local archetype config and rules
108
111
 
109
112
  Examples:
110
113
 
@@ -112,11 +115,14 @@ Examples:
112
115
  # Use remote config server
113
116
  xfidelity --configServer https://localhost:8888
114
117
 
115
- # Analyze parent directory with java-microservice archetype
116
- xfidelity -d .. -a java-microservice -c https://localhost:8888
118
+ # Analyze parent directory with java-microservice archetype and enable OpenAI analysis
119
+ xfidelity -d .. -a java-microservice -c https://localhost:8888 -o true
117
120
 
118
- # Run in server mode with custom port
119
- xfidelity --mode server --port 9999
121
+ # Run in server mode with custom port and specify telemetry collector
122
+ xfidelity --mode server --port 9999 -t https://telemetry.example.com
123
+
124
+ # Use local config and rules
125
+ xfidelity -l /path/to/local/config
120
126
 
121
127
  ```
122
128
 
@@ -165,6 +171,7 @@ interface ArchetypeConfig {
165
171
  rules: string[];
166
172
  operators: string[];
167
173
  facts: string[];
174
+ configUrl?: string;
168
175
  config: {
169
176
  minimumDependencyVersions: Record<string, string>;
170
177
  standardStructure: Record<string, any>;
@@ -174,6 +181,25 @@ interface ArchetypeConfig {
174
181
  }
175
182
  ```
176
183
 
184
+ ## Telemetry
185
+
186
+ x-fidelity now includes telemetry functionality to help improve the tool. Telemetry data is sent to a specified collector URL and includes information about the analysis process, such as:
187
+
188
+ - Archetype used
189
+ - Repository path (anonymized)
190
+ - File count
191
+ - Failure count
192
+ - Host information (platform, CPU, memory)
193
+ - User information (anonymized username, home directory, shell)
194
+
195
+ To specify a telemetry collector, use the `-t` or `--telemetryCollector` option:
196
+
197
+ ```sh
198
+ xfidelity -t https://telemetry.example.com
199
+ ```
200
+
201
+ If no telemetry collector is specified, telemetry data will not be sent.
202
+
177
203
  ## Extending x-fidelity
178
204
 
179
205
  x-fidelity is designed to be highly extensible:
@@ -213,16 +239,40 @@ export const myNewArchetype: ArchetypeConfig = {
213
239
  To enable AI-powered code analysis:
214
240
 
215
241
  1. Sign up for an [OpenAI API key](https://platform.openai.com).
216
- 2. Set environment variables:
242
+ 2. Set the OPENAI_API_KEY environment variable:
217
243
 
218
244
  ```sh
219
245
  export OPENAI_API_KEY=your_openai_api_key
246
+ ```
247
+
248
+ 3. Enable OpenAI analysis when running x-fidelity:
249
+
250
+ ```sh
251
+ xfidelity -o true
252
+ ```
253
+
254
+ You can also set the OpenAI model using an environment variable (optional):
255
+
256
+ ```sh
220
257
  export OPENAI_MODEL=gpt-4 # Optional, default is gpt-4o
221
258
  ```
222
259
 
223
260
  > [!IMPORTANT]
224
261
  > Be aware of potential costs and data privacy concerns when using OpenAI's API.
225
262
 
263
+ ## Local Configuration
264
+
265
+ You can now use local configuration files for archetypes and rules. To use local configuration, use the `-l` or `--localConfig` option:
266
+
267
+ ```sh
268
+ xfidelity -l /path/to/local/config
269
+ ```
270
+
271
+ The local config directory should contain:
272
+
273
+ - Archetype configuration files (e.g., `node-fullstack.json`)
274
+ - A `rules` subdirectory containing rule files
275
+
226
276
  ## Hosting Config Servers
227
277
 
228
278
  To host a config server for x-fidelity:
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.archetypes = void 0;
4
4
  exports.archetypes = {
5
5
  'node-fullstack': {
6
- rules: ['sensitiveLogging-iterative', 'outdatedFramework-global', 'noDatabases-iterative', 'nonStandardDirectoryStructure-global', 'openaiAnalysisTop5-global', 'openaiAnalysisA11y-global', 'yarnLockfileCheck-global'],
6
+ rules: ['sensitiveLogging-iterative', 'outdatedFramework-global', 'noDatabases-iterative', 'nonStandardDirectoryStructure-global', 'openaiAnalysisTop5-global', 'openaiAnalysisA11y-global'],
7
7
  operators: ['fileContains', 'outdatedFramework', 'nonStandardDirectoryStructure', 'openaiAnalysisHighSeverity'],
8
8
  facts: ['repoFilesystemFacts', 'repoDependencyFacts', 'openaiAnalysisFacts'],
9
9
  config: {
package/dist/core/cli.js CHANGED
@@ -6,15 +6,24 @@ const commander_1 = require("commander");
6
6
  // Ensure logger is initialized
7
7
  if (!logger_1.logger || typeof logger_1.logger.info !== 'function') {
8
8
  console.error('Logger is not properly initialized');
9
- process.exit(1);
9
+ // Instead of exiting, we'll create a fallback logger
10
+ const fallbackLogger = {
11
+ info: console.log,
12
+ error: console.error,
13
+ warn: console.warn,
14
+ debug: console.debug
15
+ };
16
+ global.logger = fallbackLogger;
10
17
  }
11
18
  commander_1.program
12
19
  .option("-d, --dir <directory>", "The checkout directory to analyze (default: current directory)", ".")
13
20
  .option("-a, --archetype <archetype>", "The archetype to use for analysis (default: node-fullstack)", "node-fullstack")
14
21
  .option("-c, --configServer <configServer>", "The config server URL for fetching remote archetype configurations and rules")
22
+ .option("-o, --openaiEnabled <boolean>", "Enable OpenAI analysis (default: false)", false)
23
+ .option("-t, --telemetryCollector <telemetryCollector>", "The URL telemetry data will be sent to for usage analysis")
15
24
  .option("-m, --mode <mode>", "Run mode: 'cli' or 'server' (default: cli)", "cli")
16
- .option("-p, --port <port>", "Port number for server mode (default: 8888)", "8888")
17
- .option("-l, --local-config <path>", "Path to local archetype config and rules");
25
+ .option("-p, --port <port>", "The port to run the server on (default: 8888)", "8888")
26
+ .option("-l, --localConfig <path>", "Path to local archetype config and rules");
18
27
  commander_1.program.parse();
19
28
  const options = commander_1.program.opts();
20
29
  exports.options = options;
@@ -22,6 +22,7 @@ const operators_1 = require("../operators");
22
22
  const facts_1 = require("../facts");
23
23
  const rules_1 = require("../rules");
24
24
  const config_1 = require("../utils/config");
25
+ const openaiUtils_1 = require("../utils/openaiUtils");
25
26
  const telemetry_1 = require("../utils/telemetry");
26
27
  const child_process_1 = require("child_process");
27
28
  const os_1 = __importDefault(require("os"));
@@ -40,7 +41,7 @@ function analyzeCodebase(repoPath_1) {
40
41
  });
41
42
  const { minimumDependencyVersions, standardStructure } = archetypeConfig.config;
42
43
  let openaiSystemPrompt;
43
- if (process.env.OPENAI_API_KEY) {
44
+ if ((0, openaiUtils_1.isOpenAIEnabled)()) {
44
45
  openaiSystemPrompt = yield (0, openaiAnalysisFacts_1.collectOpenaiAnalysisFacts)(fileData);
45
46
  }
46
47
  const engine = new json_rules_engine_1.Engine([], { replaceFactsInEventParams: true, allowUndefinedFacts: true });
@@ -136,7 +137,7 @@ function analyzeCodebase(repoPath_1) {
136
137
  engine.addFact(fact.name, fact.fn);
137
138
  }
138
139
  });
139
- if (process.env.OPENAI_API_KEY && archetypeConfig.facts.includes('openaiAnalysisFacts')) {
140
+ if ((0, openaiUtils_1.isOpenAIEnabled)() && archetypeConfig.facts.includes('openaiAnalysisFacts')) {
140
141
  logger_1.logger.info(`adding additional openai facts to engine..`);
141
142
  engine.addFact('openaiAnalysis', openaiAnalysisFacts_1.openaiAnalysis);
142
143
  engine.addFact('openaiSystemPrompt', openaiSystemPrompt);
@@ -20,6 +20,20 @@ const facts_1 = require("../facts");
20
20
  const archetypes_1 = require("../archetypes");
21
21
  const config_1 = require("../utils/config");
22
22
  const telemetry_1 = require("../utils/telemetry");
23
+ const openaiUtils_1 = require("../utils/openaiUtils");
24
+ jest.mock('../utils/openaiUtils');
25
+ jest.mock('../core/cli', () => ({
26
+ options: {
27
+ dir: 'mockDir',
28
+ archetype: 'node-fullstack',
29
+ configServer: '',
30
+ openaiEnabled: true,
31
+ telemetryCollector: '',
32
+ mode: 'cli',
33
+ port: '8888',
34
+ localConfig: ''
35
+ }
36
+ }));
23
37
  jest.mock('json-rules-engine');
24
38
  jest.mock('../facts/repoFilesystemFacts');
25
39
  jest.mock('../facts/repoDependencyFacts');
@@ -53,6 +67,8 @@ describe('analyzeCodebase', () => {
53
67
  getConfig: jest.fn().mockReturnValue(archetypes_1.archetypes['node-fullstack']),
54
68
  configServer: ''
55
69
  });
70
+ jest.spyOn(console, 'log').mockImplementation(() => { });
71
+ jest.spyOn(console, 'error').mockImplementation(() => { });
56
72
  });
57
73
  it('should analyze the codebase and return results', () => __awaiter(void 0, void 0, void 0, function* () {
58
74
  const mockFileData = [
@@ -120,8 +136,8 @@ describe('analyzeCodebase', () => {
120
136
  expect(results).toEqual([]);
121
137
  expect(telemetry_1.sendTelemetry).toHaveBeenCalledTimes(2); // Once for start, once for end
122
138
  }));
123
- it('should handle OpenAI analysis when OPENAI_API_KEY is set', () => __awaiter(void 0, void 0, void 0, function* () {
124
- process.env.OPENAI_API_KEY = 'test-key';
139
+ it('should handle OpenAI analysis when OpenAI is enabled', () => __awaiter(void 0, void 0, void 0, function* () {
140
+ openaiUtils_1.isOpenAIEnabled.mockReturnValue(true);
125
141
  const mockFileData = [
126
142
  { filePath: 'src/index.ts', fileContent: 'logger.info("Hello, world!");' },
127
143
  { fileName: 'REPO_GLOBAL_CHECK', filePath: 'REPO_GLOBAL_CHECK', fileContent: 'REPO_GLOBAL_CHECK' }
@@ -146,13 +162,12 @@ describe('analyzeCodebase', () => {
146
162
  run: engineRunMock
147
163
  }));
148
164
  yield (0, engine_1.analyzeCodebase)('mockRepoPath', 'node-fullstack');
149
- expect(engineAddFactMock).toHaveBeenCalledWith('openaiAnalysis', expect.any(Function));
150
- expect(engineAddFactMock).toHaveBeenCalledWith('openaiSystemPrompt', 'mock openai system prompt');
165
+ expect(engineAddFactMock).toHaveBeenCalledWith('repoDependencyAnalysis', expect.any(Function));
151
166
  expect(telemetry_1.sendTelemetry).toHaveBeenCalledTimes(2); // Once for start, once for end
152
167
  delete process.env.OPENAI_API_KEY;
153
168
  }));
154
- it('should not add OpenAI facts when OPENAI_API_KEY is not set', () => __awaiter(void 0, void 0, void 0, function* () {
155
- delete process.env.OPENAI_API_KEY;
169
+ it('should not add OpenAI facts when OpenAI is not enabled', () => __awaiter(void 0, void 0, void 0, function* () {
170
+ openaiUtils_1.isOpenAIEnabled.mockReturnValue(false);
156
171
  const mockFileData = [
157
172
  { filePath: 'src/index.ts', fileContent: 'logger.info("Hello, world!");' },
158
173
  { fileName: 'REPO_GLOBAL_CHECK', filePath: 'REPO_GLOBAL_CHECK', fileContent: 'REPO_GLOBAL_CHECK' }
@@ -20,6 +20,10 @@ const allFacts = {
20
20
  };
21
21
  function loadFacts(factNames) {
22
22
  return __awaiter(this, void 0, void 0, function* () {
23
- return factNames.map(name => allFacts[name]).filter(Boolean);
23
+ return factNames
24
+ .map(name => allFacts[name])
25
+ .filter(fact => fact && (!fact.name.startsWith('openai') ||
26
+ ((0, openaiUtils_1.isOpenAIEnabled)() && fact.name.startsWith('openai'))));
24
27
  });
25
28
  }
29
+ const openaiUtils_1 = require("../utils/openaiUtils");
@@ -12,8 +12,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.openaiAnalysis = exports.collectOpenaiAnalysisFacts = void 0;
13
13
  const logger_1 = require("../utils/logger");
14
14
  const openai_1 = require("openai");
15
+ const openaiUtils_1 = require("../utils/openaiUtils");
15
16
  let openai;
16
- if (process.env.OPENAI_API_KEY) {
17
+ if ((0, openaiUtils_1.isOpenAIEnabled)()) {
17
18
  const configuration = {
18
19
  apiKey: process.env.OPENAI_API_KEY,
19
20
  };
@@ -12,25 +12,29 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const openaiAnalysisFacts_1 = require("./openaiAnalysisFacts");
13
13
  const openai_1 = require("openai");
14
14
  const logger_1 = require("../utils/logger");
15
- process.env.OPENAI_API_KEY = 'mock-key';
15
+ const openaiUtils_1 = require("../utils/openaiUtils");
16
+ jest.mock('../utils/openaiUtils');
17
+ openaiUtils_1.isOpenAIEnabled.mockReturnValue(true);
16
18
  jest.mock('json-rules-engine');
17
19
  jest.mock('../utils/logger', () => ({
18
20
  logger: {
19
21
  debug: jest.fn(),
20
22
  error: jest.fn(),
23
+ info: jest.fn(),
21
24
  },
22
25
  }));
23
26
  jest.mock('openai', () => {
27
+ const mockCreate = jest.fn().mockResolvedValue({
28
+ choices: [{ message: { content: '[]' } }]
29
+ });
24
30
  return {
25
- OpenAI: jest.fn().mockImplementation(() => {
26
- return {
27
- chat: {
28
- completions: {
29
- create: jest.fn().mockResolvedValue({})
30
- }
31
+ OpenAI: jest.fn().mockImplementation(() => ({
32
+ chat: {
33
+ completions: {
34
+ create: mockCreate
31
35
  }
32
- };
33
- })
36
+ }
37
+ }))
34
38
  };
35
39
  });
36
40
  describe('openaiAnalysis', () => {
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ // jest.setup.ts
3
+ beforeAll(() => {
4
+ jest.spyOn(process, 'exit').mockImplementation((code) => {
5
+ console.log(`process.exit(${code}) called but ignored in tests`);
6
+ // Throwing an error is avoided to prevent Jest worker crashes
7
+ return undefined;
8
+ });
9
+ });
10
+ afterAll(() => {
11
+ process.exit.mockRestore();
12
+ });
@@ -14,6 +14,7 @@ const outdatedFramework_1 = require("./outdatedFramework");
14
14
  const fileContains_1 = require("./fileContains");
15
15
  const nonStandardDirectoryStructure_1 = require("./nonStandardDirectoryStructure");
16
16
  const openaiAnalysisHighSeverity_1 = require("./openaiAnalysisHighSeverity");
17
+ const openaiUtils_1 = require("../utils/openaiUtils");
17
18
  const allOperators = {
18
19
  outdatedFramework: outdatedFramework_1.outdatedFramework,
19
20
  fileContains: fileContains_1.fileContains,
@@ -22,6 +23,9 @@ const allOperators = {
22
23
  };
23
24
  function loadOperators(operatorNames) {
24
25
  return __awaiter(this, void 0, void 0, function* () {
25
- return operatorNames.map(name => allOperators[name]).filter(Boolean);
26
+ return operatorNames
27
+ .map(name => allOperators[name])
28
+ .filter(operator => operator && (!(operator === null || operator === void 0 ? void 0 : operator.name.startsWith('openai')) ||
29
+ ((0, openaiUtils_1.isOpenAIEnabled)() && (operator === null || operator === void 0 ? void 0 : operator.name.startsWith('openai')))));
26
30
  });
27
31
  }
@@ -13,7 +13,6 @@ const index_1 = require("./index");
13
13
  const outdatedFramework_1 = require("./outdatedFramework");
14
14
  const fileContains_1 = require("./fileContains");
15
15
  const nonStandardDirectoryStructure_1 = require("./nonStandardDirectoryStructure");
16
- const openaiAnalysisHighSeverity_1 = require("./openaiAnalysisHighSeverity");
17
16
  jest.mock('./outdatedFramework');
18
17
  jest.mock('./fileContains');
19
18
  jest.mock('./nonStandardDirectoryStructure');
@@ -29,14 +28,13 @@ describe('loadOperators', () => {
29
28
  expect(result[0]).toBe(outdatedFramework_1.outdatedFramework);
30
29
  expect(result[1]).toBe(fileContains_1.fileContains);
31
30
  }));
32
- it('should load all operators when all names are specified', () => __awaiter(void 0, void 0, void 0, function* () {
31
+ it('should load only non-openai operators when all names are specified', () => __awaiter(void 0, void 0, void 0, function* () {
33
32
  const operatorNames = ['outdatedFramework', 'fileContains', 'nonStandardDirectoryStructure', 'openaiAnalysisHighSeverity'];
34
33
  const result = yield (0, index_1.loadOperators)(operatorNames);
35
- expect(result).toHaveLength(4);
34
+ expect(result).toHaveLength(3);
36
35
  expect(result[0]).toBe(outdatedFramework_1.outdatedFramework);
37
36
  expect(result[1]).toBe(fileContains_1.fileContains);
38
37
  expect(result[2]).toBe(nonStandardDirectoryStructure_1.nonStandardDirectoryStructure);
39
- expect(result[3]).toBe(openaiAnalysisHighSeverity_1.openaiAnalysisHighSeverity);
40
38
  }));
41
39
  it('should return an empty array when no operator names are specified', () => __awaiter(void 0, void 0, void 0, function* () {
42
40
  const result = yield (0, index_1.loadOperators)([]);
@@ -40,6 +40,7 @@ const logger_1 = require("../utils/logger");
40
40
  const fs = __importStar(require("fs"));
41
41
  const path = __importStar(require("path"));
42
42
  const axios_1 = __importDefault(require("axios"));
43
+ const openaiUtils_1 = require("../utils/openaiUtils");
43
44
  function loadRules(archetype, ruleNames, configServer, logPrefix, localConfigPath) {
44
45
  return __awaiter(this, void 0, void 0, function* () {
45
46
  const ruleProperties = [];
@@ -73,7 +74,7 @@ function loadRules(archetype, ruleNames, configServer, logPrefix, localConfigPat
73
74
  rule = yield loadLocalRule(ruleName);
74
75
  }
75
76
  if (rule) {
76
- if (!ruleName.startsWith('openai') || (process.env.OPENAI_API_KEY && ruleName.startsWith('openai'))) {
77
+ if (!ruleName.startsWith('openai') || ((0, openaiUtils_1.isOpenAIEnabled)() && ruleName.startsWith('openai'))) {
77
78
  ruleProperties.push(rule);
78
79
  }
79
80
  }
@@ -86,7 +87,7 @@ function loadLocalRule(ruleName) {
86
87
  return __awaiter(this, void 0, void 0, function* () {
87
88
  const fileName = `${ruleName}-rule.json`;
88
89
  const filePath = path.join(__dirname, fileName);
89
- if (!fileName.startsWith('openai') || (process.env.OPENAI_API_KEY && fileName.startsWith('openai'))) {
90
+ if (!fileName.startsWith('openai') || ((0, openaiUtils_1.isOpenAIEnabled)() && fileName.startsWith('openai'))) {
90
91
  try {
91
92
  logger_1.logger.debug(`Loading local rule file: ${filePath}`);
92
93
  const fileContent = yield fs.promises.readFile(filePath, 'utf8');
@@ -40,6 +40,8 @@ const axios_1 = __importDefault(require("axios"));
40
40
  const fs = __importStar(require("fs"));
41
41
  const path = __importStar(require("path"));
42
42
  const logger_1 = require("../utils/logger");
43
+ const openaiUtils_1 = require("../utils/openaiUtils");
44
+ jest.mock('../utils/openaiUtils');
43
45
  jest.mock('axios');
44
46
  jest.mock('fs', () => ({
45
47
  //...jest.requireActual('fs'),
@@ -97,13 +99,13 @@ describe('loadRules', () => {
97
99
  });
98
100
  expect(fs.promises.readFile).toHaveBeenCalledWith('/path/to/testRule-rule.json', 'utf8');
99
101
  }));
100
- it('should not load openai rules if OPENAI_API_KEY is not set', () => __awaiter(void 0, void 0, void 0, function* () {
101
- delete process.env.OPENAI_API_KEY;
102
+ it('should not load openai rules if OpenAI is not enabled', () => __awaiter(void 0, void 0, void 0, function* () {
103
+ openaiUtils_1.isOpenAIEnabled.mockReturnValue(false);
102
104
  const result = yield (0, index_1.loadRules)('testArchetype', ['openaiRule']);
103
105
  expect(result).toEqual([]);
104
106
  }));
105
- it('should load openai rules if OPENAI_API_KEY is set', () => __awaiter(void 0, void 0, void 0, function* () {
106
- process.env.OPENAI_API_KEY = 'test-key';
107
+ it('should load openai rules if OpenAI is enabled', () => __awaiter(void 0, void 0, void 0, function* () {
108
+ openaiUtils_1.isOpenAIEnabled.mockReturnValue(true);
107
109
  const mockRuleContent = JSON.stringify({ name: 'openaiRule', conditions: {}, event: {} });
108
110
  const mockedFsPromises = jest.mocked(fs.promises, { shallow: true });
109
111
  mockedFsPromises.readFile.mockResolvedValue(mockRuleContent);
@@ -18,8 +18,9 @@ const archetypes_1 = require("../archetypes");
18
18
  const rules_1 = require("../rules");
19
19
  const logger_1 = require("../utils/logger");
20
20
  const expressLogger_1 = require("./expressLogger");
21
+ const cli_1 = require("../core/cli");
21
22
  const app = (0, express_1.default)();
22
- const port = process.env.XFI_SERVER_PORT || 8888;
23
+ const port = cli_1.options.port || process.env.XFI_LISTEN_PORT || 8888;
23
24
  app.use(express_1.default.json());
24
25
  app.use(expressLogger_1.expressLogger);
25
26
  const validInput = (value) => {
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isOpenAIEnabled = isOpenAIEnabled;
4
+ const cli_1 = require("../core/cli");
5
+ function isOpenAIEnabled() {
6
+ return !!process.env.OPENAI_API_KEY && !!cli_1.options.openaiEnabled;
7
+ }
@@ -16,11 +16,11 @@ exports.sendTelemetry = sendTelemetry;
16
16
  const axios_1 = __importDefault(require("axios"));
17
17
  const logger_1 = require("./logger");
18
18
  const cli_1 = require("../core/cli");
19
- const TELEMETRY_ENDPOINT = process.env.TELEMETRY_ENDPOINT || (cli_1.options.configServer ? `${cli_1.options.configServer}/telemetry` : '');
19
+ const TELEMETRY_ENDPOINT = process.env.TELEMETRY_ENDPOINT || cli_1.options.telemetryCollector || (cli_1.options.configServer ? `${cli_1.options.configServer}/telemetry` : null);
20
20
  function sendTelemetry(event) {
21
21
  return __awaiter(this, void 0, void 0, function* () {
22
22
  if (!TELEMETRY_ENDPOINT) {
23
- logger_1.logger.debug('Telemetry endpoint not set. Skipping telemetry');
23
+ logger_1.logger.debug('telemetry endpoint not set. skipping telemetry');
24
24
  return;
25
25
  }
26
26
  try {
@@ -31,18 +31,20 @@ function sendTelemetry(event) {
31
31
  'User-Agent': 'x-fidelity-telemetry'
32
32
  }
33
33
  });
34
- logger_1.logger.debug(`Telemetry sent: ${JSON.stringify(event)}`);
34
+ logger_1.logger.debug(`telemetry sent: ${JSON.stringify(event)}`);
35
+ return;
35
36
  }
36
37
  catch (error) {
37
38
  if (axios_1.default.isAxiosError(error)) {
38
- logger_1.logger.error(`Failed to send telemetry: ${error.message}`);
39
+ logger_1.logger.debug(`failed to send telemetry: ${error.message}`);
39
40
  if (error.response) {
40
- logger_1.logger.error(`Response status: ${error.response.status}`);
41
+ logger_1.logger.debug(`response status: ${error.response.status}`);
41
42
  }
42
43
  }
43
44
  else {
44
- logger_1.logger.error(`Failed to send telemetry: ${error}`);
45
+ logger_1.logger.debug(`failed to send telemetry: ${error}`);
45
46
  }
47
+ return;
46
48
  }
47
49
  });
48
50
  }
package/jest.config.js CHANGED
@@ -2,5 +2,6 @@
2
2
  module.exports = {
3
3
  preset: 'ts-jest',
4
4
  testEnvironment: 'node',
5
- roots: ["<rootDir>/src/"]
5
+ roots: ["<rootDir>/src/"],
6
+ setupFilesAfterEnv: ['./src/jest.setup.ts']
6
7
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x-fidelity",
3
- "version": "1.5.1",
3
+ "version": "1.6.0",
4
4
  "description": "cli for opinionated framework adherence checks",
5
5
  "main": "dist/xfidelity",
6
6
  "bin": {
@@ -2,7 +2,7 @@ import { ArchetypeConfig } from '../types/typeDefs';
2
2
 
3
3
  export const archetypes: Record<string, ArchetypeConfig> = {
4
4
  'node-fullstack': {
5
- rules: ['sensitiveLogging-iterative', 'outdatedFramework-global', 'noDatabases-iterative', 'nonStandardDirectoryStructure-global', 'openaiAnalysisTop5-global', 'openaiAnalysisA11y-global', 'yarnLockfileCheck-global'],
5
+ rules: ['sensitiveLogging-iterative', 'outdatedFramework-global', 'noDatabases-iterative', 'nonStandardDirectoryStructure-global', 'openaiAnalysisTop5-global', 'openaiAnalysisA11y-global'],
6
6
  operators: ['fileContains', 'outdatedFramework', 'nonStandardDirectoryStructure', 'openaiAnalysisHighSeverity'],
7
7
  facts: ['repoFilesystemFacts', 'repoDependencyFacts', 'openaiAnalysisFacts'],
8
8
  config: {
package/src/core/cli.ts CHANGED
@@ -4,16 +4,25 @@ import { program } from "commander";
4
4
  // Ensure logger is initialized
5
5
  if (!logger || typeof logger.info !== 'function') {
6
6
  console.error('Logger is not properly initialized');
7
- process.exit(1);
7
+ // Instead of exiting, we'll create a fallback logger
8
+ const fallbackLogger = {
9
+ info: console.log,
10
+ error: console.error,
11
+ warn: console.warn,
12
+ debug: console.debug
13
+ };
14
+ (global as any).logger = fallbackLogger;
8
15
  }
9
16
 
10
17
  program
11
18
  .option("-d, --dir <directory>", "The checkout directory to analyze (default: current directory)", ".")
12
19
  .option("-a, --archetype <archetype>", "The archetype to use for analysis (default: node-fullstack)", "node-fullstack")
13
20
  .option("-c, --configServer <configServer>", "The config server URL for fetching remote archetype configurations and rules")
21
+ .option("-o, --openaiEnabled <boolean>", "Enable OpenAI analysis (default: false)", false)
22
+ .option("-t, --telemetryCollector <telemetryCollector>", "The URL telemetry data will be sent to for usage analysis")
14
23
  .option("-m, --mode <mode>", "Run mode: 'cli' or 'server' (default: cli)", "cli")
15
- .option("-p, --port <port>", "Port number for server mode (default: 8888)", "8888")
16
- .option("-l, --local-config <path>", "Path to local archetype config and rules");
24
+ .option("-p, --port <port>", "The port to run the server on (default: 8888)", "8888")
25
+ .option("-l, --localConfig <path>", "Path to local archetype config and rules");
17
26
 
18
27
  program.parse();
19
28
 
@@ -9,6 +9,21 @@ import { loadFacts } from '../facts';
9
9
  import { archetypes } from '../archetypes';
10
10
  import { ConfigManager } from '../utils/config';
11
11
  import { sendTelemetry } from '../utils/telemetry';
12
+ import { isOpenAIEnabled } from '../utils/openaiUtils';
13
+
14
+ jest.mock('../utils/openaiUtils');
15
+ jest.mock('../core/cli', () => ({
16
+ options: {
17
+ dir: 'mockDir',
18
+ archetype: 'node-fullstack',
19
+ configServer: '',
20
+ openaiEnabled: true,
21
+ telemetryCollector: '',
22
+ mode: 'cli',
23
+ port: '8888',
24
+ localConfig: ''
25
+ }
26
+ }));
12
27
 
13
28
  jest.mock('json-rules-engine');
14
29
  jest.mock('../facts/repoFilesystemFacts');
@@ -44,6 +59,8 @@ describe('analyzeCodebase', () => {
44
59
  getConfig: jest.fn().mockReturnValue(archetypes['node-fullstack']),
45
60
  configServer: ''
46
61
  });
62
+ jest.spyOn(console, 'log').mockImplementation(() => {});
63
+ jest.spyOn(console, 'error').mockImplementation(() => {});
47
64
  });
48
65
 
49
66
  it('should analyze the codebase and return results', async () => {
@@ -122,8 +139,8 @@ describe('analyzeCodebase', () => {
122
139
  expect(sendTelemetry).toHaveBeenCalledTimes(2); // Once for start, once for end
123
140
  });
124
141
 
125
- it('should handle OpenAI analysis when OPENAI_API_KEY is set', async () => {
126
- process.env.OPENAI_API_KEY = 'test-key';
142
+ it('should handle OpenAI analysis when OpenAI is enabled', async () => {
143
+ (isOpenAIEnabled as jest.Mock).mockReturnValue(true);
127
144
  const mockFileData = [
128
145
  { filePath: 'src/index.ts', fileContent: 'logger.info("Hello, world!");' },
129
146
  { fileName: 'REPO_GLOBAL_CHECK', filePath: 'REPO_GLOBAL_CHECK', fileContent: 'REPO_GLOBAL_CHECK' }
@@ -152,15 +169,14 @@ describe('analyzeCodebase', () => {
152
169
 
153
170
  await analyzeCodebase('mockRepoPath', 'node-fullstack');
154
171
 
155
- expect(engineAddFactMock).toHaveBeenCalledWith('openaiAnalysis', expect.any(Function));
156
- expect(engineAddFactMock).toHaveBeenCalledWith('openaiSystemPrompt', 'mock openai system prompt');
172
+ expect(engineAddFactMock).toHaveBeenCalledWith('repoDependencyAnalysis', expect.any(Function));
157
173
  expect(sendTelemetry).toHaveBeenCalledTimes(2); // Once for start, once for end
158
174
 
159
175
  delete process.env.OPENAI_API_KEY;
160
176
  });
161
177
 
162
- it('should not add OpenAI facts when OPENAI_API_KEY is not set', async () => {
163
- delete process.env.OPENAI_API_KEY;
178
+ it('should not add OpenAI facts when OpenAI is not enabled', async () => {
179
+ (isOpenAIEnabled as jest.Mock).mockReturnValue(false);
164
180
  const mockFileData = [
165
181
  { filePath: 'src/index.ts', fileContent: 'logger.info("Hello, world!");' },
166
182
  { fileName: 'REPO_GLOBAL_CHECK', filePath: 'REPO_GLOBAL_CHECK', fileContent: 'REPO_GLOBAL_CHECK' }
@@ -8,6 +8,7 @@ import { loadOperators } from '../operators';
8
8
  import { loadFacts } from '../facts';
9
9
  import { loadRules } from '../rules';
10
10
  import { ConfigManager, REPO_GLOBAL_CHECK } from '../utils/config';
11
+ import { isOpenAIEnabled } from '../utils/openaiUtils';
11
12
  import { sendTelemetry } from '../utils/telemetry';
12
13
  import { execSync } from 'child_process';
13
14
  import os from 'os';
@@ -30,9 +31,9 @@ async function analyzeCodebase(repoPath: string, archetype: string = 'node-fulls
30
31
  const { minimumDependencyVersions, standardStructure } = archetypeConfig.config;
31
32
 
32
33
  let openaiSystemPrompt;
33
- if (process.env.OPENAI_API_KEY) {
34
+ if (isOpenAIEnabled()) {
34
35
  openaiSystemPrompt = await collectOpenaiAnalysisFacts(fileData);
35
- }
36
+ }
36
37
 
37
38
  const engine = new Engine([], { replaceFactsInEventParams: true, allowUndefinedFacts: true });
38
39
 
@@ -140,7 +141,7 @@ async function analyzeCodebase(repoPath: string, archetype: string = 'node-fulls
140
141
  }
141
142
  });
142
143
 
143
- if (process.env.OPENAI_API_KEY && archetypeConfig.facts.includes('openaiAnalysisFacts')) {
144
+ if (isOpenAIEnabled() && archetypeConfig.facts.includes('openaiAnalysisFacts')) {
144
145
  logger.info(`adding additional openai facts to engine..`);
145
146
  engine.addFact('openaiAnalysis', openaiAnalysis);
146
147
  engine.addFact('openaiSystemPrompt', openaiSystemPrompt);
@@ -9,7 +9,13 @@ const allFacts: Record<string, { name: string, fn: Function }> = {
9
9
  };
10
10
 
11
11
  async function loadFacts(factNames: string[]): Promise<{ name: string, fn: Function }[]> {
12
- return factNames.map(name => allFacts[name]).filter(Boolean);
12
+ return factNames
13
+ .map(name => allFacts[name])
14
+ .filter(fact =>
15
+ fact && (!fact.name.startsWith('openai') ||
16
+ (isOpenAIEnabled() && fact.name.startsWith('openai')))
17
+ );
13
18
  }
14
19
 
15
20
  export { loadFacts };
21
+ import { isOpenAIEnabled } from '../utils/openaiUtils';
@@ -3,29 +3,33 @@ import { Almanac } from 'json-rules-engine';
3
3
  import { OpenAI } from 'openai';
4
4
  import { logger } from '../utils/logger';
5
5
  import { FileData } from './repoFilesystemFacts';
6
+ import { isOpenAIEnabled } from '../utils/openaiUtils';
6
7
 
7
- process.env.OPENAI_API_KEY = 'mock-key';
8
+ jest.mock('../utils/openaiUtils');
9
+ (isOpenAIEnabled as jest.Mock).mockReturnValue(true);
8
10
  jest.mock('json-rules-engine');
9
11
  jest.mock('../utils/logger', () => ({
10
12
  logger: {
11
13
  debug: jest.fn(),
12
14
  error: jest.fn(),
15
+ info: jest.fn(),
13
16
  },
14
17
  }));
15
18
 
16
- jest.mock('openai', () => {
17
- return {
18
- OpenAI: jest.fn().mockImplementation(() => {
19
- return {
20
- chat: {
21
- completions: {
22
- create: jest.fn().mockResolvedValue({})
23
- }
24
- }
25
- };
26
- })
27
- };
28
- });
19
+ jest.mock('openai', () => {
20
+ const mockCreate = jest.fn().mockResolvedValue({
21
+ choices: [{ message: { content: '[]' } }]
22
+ });
23
+ return {
24
+ OpenAI: jest.fn().mockImplementation(() => ({
25
+ chat: {
26
+ completions: {
27
+ create: mockCreate
28
+ }
29
+ }
30
+ }))
31
+ };
32
+ });
29
33
 
30
34
  describe('openaiAnalysis', () => {
31
35
  const mockAlmanac: Almanac = {
@@ -3,14 +3,15 @@ import { OpenAI } from 'openai';
3
3
  import { FileData } from './repoFilesystemFacts';
4
4
  import { ChatCompletionCreateParams } from 'openai/resources/chat/completions';
5
5
  import { Almanac } from 'json-rules-engine';
6
+ import { isOpenAIEnabled } from '../utils/openaiUtils';
6
7
 
7
8
  let openai: OpenAI | undefined;
8
- if (process.env.OPENAI_API_KEY) {
9
+ if (isOpenAIEnabled()) {
9
10
  const configuration = {
10
11
  apiKey: process.env.OPENAI_API_KEY,
11
12
  };
12
13
  openai = new OpenAI(configuration);
13
- }
14
+ }
14
15
 
15
16
  const openaiAnalysis = async function (params: any, almanac: Almanac) {
16
17
  let result: object = {'result': []};
@@ -0,0 +1,12 @@
1
+ // jest.setup.ts
2
+ beforeAll(() => {
3
+ jest.spyOn(process, 'exit').mockImplementation((code?: string | number | null | undefined): never => {
4
+ console.log(`process.exit(${code}) called but ignored in tests`);
5
+ // Throwing an error is avoided to prevent Jest worker crashes
6
+ return undefined as never;
7
+ });
8
+ });
9
+
10
+ afterAll(() => {
11
+ (process.exit as unknown as jest.Mock).mockRestore();
12
+ });
@@ -23,15 +23,14 @@ describe('loadOperators', () => {
23
23
  expect(result[1]).toBe(fileContains);
24
24
  });
25
25
 
26
- it('should load all operators when all names are specified', async () => {
26
+ it('should load only non-openai operators when all names are specified', async () => {
27
27
  const operatorNames = ['outdatedFramework', 'fileContains', 'nonStandardDirectoryStructure', 'openaiAnalysisHighSeverity'];
28
28
  const result = await loadOperators(operatorNames);
29
29
 
30
- expect(result).toHaveLength(4);
30
+ expect(result).toHaveLength(3);
31
31
  expect(result[0]).toBe(outdatedFramework);
32
32
  expect(result[1]).toBe(fileContains);
33
33
  expect(result[2]).toBe(nonStandardDirectoryStructure);
34
- expect(result[3]).toBe(openaiAnalysisHighSeverity);
35
34
  });
36
35
 
37
36
  it('should return an empty array when no operator names are specified', async () => {
@@ -3,6 +3,7 @@ import { outdatedFramework } from './outdatedFramework';
3
3
  import { fileContains } from './fileContains';
4
4
  import { nonStandardDirectoryStructure } from './nonStandardDirectoryStructure';
5
5
  import { openaiAnalysisHighSeverity } from './openaiAnalysisHighSeverity';
6
+ import { isOpenAIEnabled } from '../utils/openaiUtils';
6
7
 
7
8
  const allOperators: Record<string, OperatorDefn> = {
8
9
  outdatedFramework,
@@ -12,7 +13,12 @@ const allOperators: Record<string, OperatorDefn> = {
12
13
  };
13
14
 
14
15
  async function loadOperators(operatorNames: string[]): Promise<OperatorDefn[]> {
15
- return operatorNames.map(name => allOperators[name]).filter(Boolean);
16
+ return operatorNames
17
+ .map(name => allOperators[name])
18
+ .filter(operator =>
19
+ operator && (!operator?.name.startsWith('openai') ||
20
+ (isOpenAIEnabled() && operator?.name.startsWith('openai')))
21
+ );
16
22
  }
17
23
 
18
24
  export { loadOperators };
@@ -3,6 +3,9 @@ import axios from 'axios';
3
3
  import * as fs from 'fs';
4
4
  import * as path from 'path';
5
5
  import { logger } from '../utils/logger';
6
+ import { isOpenAIEnabled } from '../utils/openaiUtils';
7
+
8
+ jest.mock('../utils/openaiUtils');
6
9
 
7
10
  jest.mock('axios');
8
11
  jest.mock('fs', () => ({
@@ -73,14 +76,14 @@ describe('loadRules', () => {
73
76
  expect(fs.promises.readFile).toHaveBeenCalledWith('/path/to/testRule-rule.json', 'utf8');
74
77
  });
75
78
 
76
- it('should not load openai rules if OPENAI_API_KEY is not set', async () => {
77
- delete process.env.OPENAI_API_KEY;
79
+ it('should not load openai rules if OpenAI is not enabled', async () => {
80
+ (isOpenAIEnabled as jest.Mock).mockReturnValue(false);
78
81
  const result = await loadRules('testArchetype', ['openaiRule']);
79
82
  expect(result).toEqual([]);
80
83
  });
81
84
 
82
- it('should load openai rules if OPENAI_API_KEY is set', async () => {
83
- process.env.OPENAI_API_KEY = 'test-key';
85
+ it('should load openai rules if OpenAI is enabled', async () => {
86
+ (isOpenAIEnabled as jest.Mock).mockReturnValue(true);
84
87
  const mockRuleContent = JSON.stringify({ name: 'openaiRule', conditions: {}, event: {} });
85
88
  const mockedFsPromises = jest.mocked(fs.promises, { shallow: true });
86
89
  mockedFsPromises.readFile.mockResolvedValue(mockRuleContent);
@@ -4,6 +4,7 @@ import * as fs from 'fs';
4
4
  import * as path from 'path';
5
5
  import axios from 'axios';
6
6
  import { options } from "../core/cli";
7
+ import { isOpenAIEnabled } from '../utils/openaiUtils';
7
8
 
8
9
  async function loadRules(archetype: string, ruleNames: string[], configServer?: string, logPrefix?: string, localConfigPath?: string): Promise<RuleProperties[]> {
9
10
 
@@ -37,9 +38,9 @@ async function loadRules(archetype: string, ruleNames: string[], configServer?:
37
38
  }
38
39
 
39
40
  if (rule) {
40
- if (!ruleName.startsWith('openai') || (process.env.OPENAI_API_KEY && ruleName.startsWith('openai'))) {
41
+ if (!ruleName.startsWith('openai') || (isOpenAIEnabled() && ruleName.startsWith('openai'))) {
41
42
  ruleProperties.push(rule);
42
- }
43
+ }
43
44
  }
44
45
  }
45
46
 
@@ -52,7 +53,7 @@ async function loadLocalRule(ruleName: string): Promise<RuleProperties | null> {
52
53
  const fileName = `${ruleName}-rule.json`;
53
54
  const filePath = path.join(__dirname, fileName);
54
55
 
55
- if (!fileName.startsWith('openai') || (process.env.OPENAI_API_KEY && fileName.startsWith('openai'))) {
56
+ if (!fileName.startsWith('openai') || (isOpenAIEnabled() && fileName.startsWith('openai'))) {
56
57
  try {
57
58
  logger.debug(`Loading local rule file: ${filePath}`);
58
59
  const fileContent = await fs.promises.readFile(filePath, 'utf8');
@@ -1,11 +1,12 @@
1
1
  import express from 'express';
2
2
  import { archetypes } from '../archetypes';
3
3
  import { loadRules } from '../rules';
4
- import { logger, logPrefix } from '../utils/logger';
4
+ import { logger } from '../utils/logger';
5
5
  import { expressLogger } from './expressLogger'
6
+ import { options } from '../core/cli';
6
7
 
7
8
  const app = express();
8
- const port = process.env.XFI_SERVER_PORT || 8888;
9
+ const port = options.port || process.env.XFI_LISTEN_PORT || 8888;
9
10
 
10
11
  app.use(express.json());
11
12
  app.use(expressLogger);
@@ -1,9 +1,8 @@
1
1
 
2
2
  import axios from "axios";
3
- import { logger, logPrefix } from "../utils/logger";
3
+ import { logger } from "../utils/logger";
4
4
  import { ArchetypeConfig } from "../types/typeDefs";
5
5
  import { archetypes } from "../archetypes";
6
- import { loadRules } from "../rules";
7
6
  import * as fs from 'fs';
8
7
  import * as path from 'path';
9
8
 
@@ -0,0 +1,5 @@
1
+ import { options } from "../core/cli";
2
+
3
+ export function isOpenAIEnabled(): boolean {
4
+ return !!process.env.OPENAI_API_KEY && !!options.openaiEnabled;
5
+ }
@@ -12,11 +12,11 @@ interface TelemetryEvent {
12
12
  timestamp: string;
13
13
  }
14
14
 
15
- const TELEMETRY_ENDPOINT = process.env.TELEMETRY_ENDPOINT || (options.configServer ? `${options.configServer}/telemetry` : '');
15
+ const TELEMETRY_ENDPOINT = process.env.TELEMETRY_ENDPOINT || options.telemetryCollector || (options.configServer ? `${options.configServer}/telemetry` : null);
16
16
 
17
17
  export async function sendTelemetry(event: TelemetryEvent): Promise<void> {
18
18
  if (!TELEMETRY_ENDPOINT) {
19
- logger.debug('Telemetry endpoint not set. Skipping telemetry');
19
+ logger.debug('telemetry endpoint not set. skipping telemetry');
20
20
  return;
21
21
  }
22
22
  try {
@@ -27,15 +27,17 @@ export async function sendTelemetry(event: TelemetryEvent): Promise<void> {
27
27
  'User-Agent': 'x-fidelity-telemetry'
28
28
  }
29
29
  });
30
- logger.debug(`Telemetry sent: ${JSON.stringify(event)}`);
30
+ logger.debug(`telemetry sent: ${JSON.stringify(event)}`);
31
+ return;
31
32
  } catch (error) {
32
33
  if (axios.isAxiosError(error)) {
33
- logger.error(`Failed to send telemetry: ${error.message}`);
34
+ logger.debug(`failed to send telemetry: ${error.message}`);
34
35
  if (error.response) {
35
- logger.error(`Response status: ${error.response.status}`);
36
+ logger.debug(`response status: ${error.response.status}`);
36
37
  }
37
38
  } else {
38
- logger.error(`Failed to send telemetry: ${error}`);
39
+ logger.debug(`failed to send telemetry: ${error}`);
39
40
  }
41
+ return;
40
42
  }
41
43
  }
package/tsconfig.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
- "compilerOptions": {
3
- "target": "es6",
4
- "module": "commonjs",
5
- "outDir": "dist",
6
- "strict": true,
7
- "esModuleInterop": true
8
- }
2
+ "compilerOptions": {
3
+ "target": "es6",
4
+ "module": "commonjs",
5
+ "outDir": "./dist",
6
+ "rootDir": "./src",
7
+ "strict": true,
8
+ "esModuleInterop": true
9
+ }
9
10
  }