flakeiq 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FlakeIQ Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,423 @@
1
+ # FlakeIQ
2
+
3
+ Test flake tracking + LLM failure classification for E2E tests. Works with **Playwright**, **Detox**, **Appium**, **Selenium**, and any framework that can output test results as JSON.
4
+
5
+ ## Prerequisites
6
+
7
+ | Requirement | Required For | Install |
8
+ |---|---|---|
9
+ | **Node.js 18+** | Reporter, CLI | [nodejs.org](https://nodejs.org/) |
10
+ | **Python 3.11+** | Dashboard, Classifier | [python.org](https://www.python.org/downloads/) |
11
+ | **Ollama** | AI failure classification | See below |
12
+
13
+ ### Install Python
14
+
15
+ ```bash
16
+ # Check if installed
17
+ python --version
18
+
19
+ # If not installed, download from:
20
+ # https://www.python.org/downloads/
21
+ ```
22
+
23
+ **Windows users:** Check "Add Python to PATH" during installation.
24
+
25
+ ### Install Ollama
26
+
27
+ Ollama runs a local LLM to classify test failures as real bugs vs flakes.
28
+
29
+ **macOS / Linux:**
30
+ ```bash
31
+ curl -fsSL https://ollama.com/install.sh | sh
32
+ ```
33
+
34
+ **Windows:**
35
+ ```bash
36
+ # Download from https://ollama.com/download
37
+ # Or use winget:
38
+ winget install Ollama.Ollama
39
+ ```
40
+
41
+ **After install, pull the model and start the server:**
42
+ ```bash
43
+ ollama pull llama3.2
44
+ ollama serve
45
+ ```
46
+
47
+ **Verify it's running:**
48
+ ```bash
49
+ curl http://localhost:11434/api/tags
50
+ ```
51
+
52
+ > **Note:** If Ollama is not running, FlakeIQ still works but failures are stored as "unclassified". Install Ollama to get AI-powered classification.
53
+
54
+ ## Quick Start
55
+
56
+ **Playwright** has a built-in reporter — zero config required. **All other frameworks** (Detox, Appium, Selenium, etc.) need a small custom reporter to output test results as JSONL. Once you have the JSONL file, `npx flakeiq classify` and `npx flakeiq serve` work identically regardless of framework.
57
+
58
+ ### Playwright (Built-in Reporter)
59
+
60
+ ```bash
61
+ npm install flakeiq --save-dev
62
+ ```
63
+
64
+ Add to your `playwright.config.ts`:
65
+ ```js
66
+ reporter: [
67
+ ['html'],
68
+ ['flakeiq/reporter', { outputFile: 'flake-results.jsonl' }],
69
+ ],
70
+ ```
71
+
72
+ Run your tests:
73
+ ```bash
74
+ npx playwright test
75
+ ```
76
+
77
+ View dashboard:
78
+ ```bash
79
+ npx flakeiq serve
80
+ ```
81
+
82
+ ### Detox (React Native)
83
+
84
+ Output test results as JSONL in this format:
85
+
86
+ ```json
87
+ {
88
+ "test_file": "src/__tests__/login.test.js",
89
+ "test_name": "login with valid credentials",
90
+ "platform": "ios",
91
+ "device_id": "iPhone 15",
92
+ "duration_ms": 4500,
93
+ "result": "passed",
94
+ "error_message": "",
95
+ "last_actions": ["tap login button", "enter email", "enter password", "tap submit"],
96
+ "classification": null,
97
+ "classification_reason": null,
98
+ "screen_name": "login",
99
+ "session_id": "detox_run_123",
100
+ "run_at": "2026-01-15T10:30:00Z"
101
+ }
102
+ ```
103
+
104
+ **Detox reporter example:**
105
+
106
+ ```javascript
107
+ // detox.config.js
108
+ module.exports = {
109
+ // ... your config
110
+ reporters: [
111
+ ['jest-html-reporter', { outputPath: 'test-results.html' }],
112
+ // Custom JSONL reporter
113
+ ['custom', {
114
+ onComplete: (results) => {
115
+ const fs = require('fs');
116
+ const lines = results.testResults.map(test => ({
117
+ test_file: test.testFilePath,
118
+ test_name: test.testTitle,
119
+ platform: process.platform,
120
+ device_id: process.env.DEVICE_ID || 'unknown',
121
+ duration_ms: test.duration,
122
+ result: test.status,
123
+ error_message: test.failureMessages.join('\n'),
124
+ last_actions: [],
125
+ classification: null,
126
+ classification_reason: null,
127
+ screen_name: test.testFilePath.split('/').pop().replace('.test.js', ''),
128
+ session_id: `detox_${Date.now()}`,
129
+ run_at: new Date().toISOString()
130
+ }));
131
+ fs.writeFileSync('flake-results.jsonl', lines.map(l => JSON.stringify(l)).join('\n'));
132
+ }
133
+ }]
134
+ ]
135
+ };
136
+ ```
137
+
138
+ ### Appium
139
+
140
+ Output test results as JSONL:
141
+
142
+ ```json
143
+ {
144
+ "test_file": "tests/android/login.js",
145
+ "test_name": "Android login flow",
146
+ "platform": "android",
147
+ "device_id": "emulator-5554",
148
+ "duration_ms": 8200,
149
+ "result": "timedOut",
150
+ "error_message": "An unknown server-side error occurred while processing the command",
151
+ "last_actions": ["findElement", "click", "sendKeys", "waitForElement"],
152
+ "classification": null,
153
+ "classification_reason": null,
154
+ "screen_name": "login",
155
+ "session_id": "appium_session_abc123",
156
+ "run_at": "2026-01-15T11:00:00Z"
157
+ }
158
+ ```
159
+
160
+ **Appium integration example:**
161
+
162
+ ```javascript
163
+ // wdio.conf.js
164
+ exports.config = {
165
+ // ... your config
166
+ onComplete: function(results) {
167
+ const fs = require('fs');
168
+ const lines = results.tests.map(test => ({
169
+ test_file: test.file,
170
+ test_name: test.title,
171
+ platform: this.capabilities.platformName,
172
+ device_id: this.capabilities.deviceName,
173
+ duration_ms: test.duration,
174
+ result: test.passed ? 'passed' : 'failed',
175
+ error_message: test.error || '',
176
+ last_actions: test.commands || [],
177
+ classification: null,
178
+ classification_reason: null,
179
+ screen_name: test.file.split('/').pop().replace('.js', ''),
180
+ session_id: `appium_${Date.now()}`,
181
+ run_at: new Date().toISOString()
182
+ }));
183
+ fs.writeFileSync('flake-results.jsonl', lines.map(l => JSON.stringify(l)).join('\n'));
184
+ }
185
+ };
186
+ ```
187
+
188
+ ### Selenium
189
+
190
+ Output test results as JSONL:
191
+
192
+ ```json
193
+ {
194
+ "test_file": "tests/selenium/login_test.py",
195
+ "test_name": "Selenium login test",
196
+ "platform": "chrome",
197
+ "device_id": "Chrome 120",
198
+ "duration_ms": 3400,
199
+ "result": "passed",
200
+ "error_message": "",
201
+ "last_actions": ["find_element", "send_keys", "click", "assert"],
202
+ "classification": null,
203
+ "classification_reason": null,
204
+ "screen_name": "login",
205
+ "session_id": "selenium_run_456",
206
+ "run_at": "2026-01-15T12:00:00Z"
207
+ }
208
+ ```
209
+
210
+ **Python Selenium integration:**
211
+
212
+ ```python
213
+ # conftest.py (pytest)
214
+ import json
215
+ import os
216
+ from datetime import datetime
217
+
218
+ def pytest_runtest_makereport(item, call):
219
+ if call.when == 'call':
220
+ result = {
221
+ 'test_file': str(item.fspath),
222
+ 'test_name': item.name,
223
+ 'platform': 'selenium',
224
+ 'device_id': os.environ.get('BROWSER', 'chrome'),
225
+ 'duration_ms': int(call.duration * 1000),
226
+ 'result': 'passed' if call.excinfo is None else 'failed',
227
+ 'error_message': str(call.excinfo.value) if call.excinfo else '',
228
+ 'last_actions': [],
229
+ 'classification': None,
230
+ 'classification_reason': None,
231
+ 'screen_name': item.name,
232
+ 'session_id': f'selenium_{int(datetime.now().timestamp())}',
233
+ 'run_at': datetime.now().isoformat() + 'Z'
234
+ }
235
+
236
+ with open('flake-results.jsonl', 'a') as f:
237
+ f.write(json.dumps(result) + '\n')
238
+ ```
239
+
240
+ ### Any Framework
241
+
242
+ For any testing framework, output results as JSONL with this schema:
243
+
244
+ ```json
245
+ {
246
+ "test_file": "string",
247
+ "test_name": "string",
248
+ "platform": "string",
249
+ "device_id": "string",
250
+ "duration_ms": 0,
251
+ "result": "passed|failed|timedOut|skipped",
252
+ "error_message": "string",
253
+ "last_actions": ["string"],
254
+ "classification": null,
255
+ "classification_reason": null,
256
+ "screen_name": "string",
257
+ "session_id": "string",
258
+ "run_at": "ISO8601"
259
+ }
260
+ ```
261
+
262
+ Then run:
263
+ ```bash
264
+ npx flakeiq classify flake-results.jsonl
265
+ npx flakeiq serve
266
+ ```
267
+
268
+ ## Every Test Run (QA / CI / Local)
269
+
270
+ ### Local / QA
271
+
272
+ ```bash
273
+ # 1. Run your Playwright tests (reporter auto-captures results)
274
+ npx playwright test
275
+
276
+ # 2. Import results into FlakeIQ database
277
+ npx flakeiq classify flake-results.jsonl
278
+
279
+ # 3. View dashboard to see flake analysis
280
+ npx flakeiq serve
281
+ ```
282
+
283
+ ### CI (GitHub Actions)
284
+
285
+ ```yaml
286
+ # .github/workflows/test.yml
287
+ name: E2E Tests
288
+ on: [push, pull_request]
289
+ jobs:
290
+ test:
291
+ runs-on: ubuntu-latest
292
+ steps:
293
+ - uses: actions/checkout@v4
294
+ - uses: actions/setup-node@v4
295
+ with:
296
+ node-version: 20
297
+ - uses: actions/setup-python@v5
298
+ with:
299
+ python-version: '3.13'
300
+ - run: npm ci
301
+ - run: npx playwright install --with-deps chromium
302
+ - run: npx playwright test
303
+ # Import results into FlakeIQ
304
+ - run: npx flakeiq classify flake-results.jsonl
305
+ # Upload results as artifact
306
+ - uses: actions/upload-artifact@v4
307
+ if: always()
308
+ with:
309
+ name: flakeiq-results
310
+ path: |
311
+ flake-results.jsonl
312
+ flakeiq-data/flake.db
313
+ ```
314
+
315
+ ### Weekly Team Review
316
+
317
+ ```bash
318
+ # View accumulated flake data over time
319
+ npx flakeiq serve
320
+
321
+ # Check flake rate trends
322
+ # Check top flaky tests
323
+ # Check device health
324
+ # Make decisions on what to fix vs ignore
325
+ ```
326
+
327
+ ## How It Works
328
+
329
+ ```
330
+ your-repo/ FlakeIQ
331
+ ========== =======
332
+
333
+ Your tests (Playwright/Detox/Appium/Selenium/any framework)
334
+ |
335
+ v (Reporter captures test results)
336
+ flake-results.jsonl ---------------> classify.py --> Ollama llama3.2 (on failures)
337
+ |
338
+ v
339
+ flake.db
340
+ |
341
+ v
342
+ dashboard.py --> browser (Chart.js)
343
+ ```
344
+
345
+ ## Commands
346
+
347
+ | Command | Requires | Description |
348
+ |---|---|---|
349
+ | `npx flakeiq serve` | Python | Start the dashboard server |
350
+ | `npx flakeiq serve --port 3000` | Python | Start on a custom port |
351
+ | `npx flakeiq serve --seed` | Python | Dashboard with demo data |
352
+ | `npx flakeiq serve --open` | Python | Auto-open browser |
353
+ | `npx flakeiq classify results.jsonl` | Python + Ollama | Classify failures from JSONL |
354
+ | `npx flakeiq seed` | Python | Generate synthetic seed data |
355
+ | `npx flakeiq status` | — | Show environment status |
356
+ | `npx flakeiq reporter` | — | Show reporter setup instructions |
357
+
358
+ ## Dashboard Options
359
+
360
+ | Flag | Description |
361
+ |---|---|
362
+ | `--db PATH` | SQLite database path (default: `flakeiq-data/flake.db`) |
363
+ | `--port NUM` | HTTP port (default: `8080`) |
364
+ | `--host ADDR` | Bind address (default: `127.0.0.1`) |
365
+ | `--seed` | Generate and use demo data |
366
+ | `--open` | Auto-launch browser on start |
367
+
368
+ ## API Endpoints
369
+
370
+ All endpoints return JSON. Used by the dashboard's Chart.js frontend.
371
+
372
+ | Endpoint | Description |
373
+ |---|---|
374
+ | `/api/stats` | Total runs, failures, flake rate, avg duration, date range |
375
+ | `/api/sessions` | All test sessions with pass/fail counts, avg duration, time range |
376
+ | `/api/latest-session` | Latest session + per-test results |
377
+ | `/api/flake-rate` | Daily flake rate trend (last 60 days) |
378
+ | `/api/breakdown` | Classification category counts for failed tests |
379
+ | `/api/by-action` | Flake rate grouped by last action type |
380
+ | `/api/by-platform` | Pass/fail counts per platform |
381
+ | `/api/volume` | Daily pass/fail test volume |
382
+ | `/api/duration-dist` | Failure duration bucketed histogram |
383
+ | `/api/classification-trend` | Classification counts per day over time |
384
+ | `/api/heatmap` | Screen x day flake rate grid |
385
+ | `/api/top-flakes` | Flakiest tests ranked by fail rate |
386
+ | `/api/devices` | Device-level pass/fail stats |
387
+
388
+ ## Seed Data
389
+
390
+ For demo without real test runs:
391
+
392
+ ```bash
393
+ npx flakeiq seed
394
+ npx flakeiq serve --seed
395
+ ```
396
+
397
+ ## Environment Variables
398
+
399
+ | Variable | Default | Description |
400
+ |---|---|---|
401
+ | `FLAKE_DB` | `./flakeiq-data/flake.db` | SQLite database path |
402
+ | `FLAKE_PORT` | `8080` | Dashboard port |
403
+ | `FLAKE_HOST` | `127.0.0.1` | Dashboard bind address |
404
+ | `OLLAMA_URL` | `http://localhost:11434/api/generate` | Ollama API URL |
405
+ | `OLLAMA_MODEL` | `llama3.2` | Ollama model name |
406
+ | `FLAKE_DATA_DIR` | `./flakeiq-data` | Data directory |
407
+
408
+ ## Files
409
+
410
+ | File | Purpose |
411
+ |---|---|
412
+ | `reporter/flake-reporter.js` | Playwright reporter — captures last 10 `pw:api` steps, writes JSONL |
413
+ | `reporter/index.js` | Re-export for `flakeiq/reporter` import |
414
+ | `python/classify.py` | Reads JSONL, calls Ollama, upserts SQLite |
415
+ | `python/dashboard.py` | HTTP server with Chart.js dashboard |
416
+ | `python/seed.py` | Generates 5100 synthetic records for demo |
417
+ | `python/web/static/` | Dashboard HTML/CSS/JS assets |
418
+ | `lib/*.js` | CLI commands (serve, classify, seed, status, reporter, setup) |
419
+ | `bin/flakeiq.js` | CLI entry point |
420
+
421
+ ## License
422
+
423
+ MIT
package/bin/flakeiq.js ADDED
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { getConfig, isVenvReady } = require('../lib/config');
4
+ const { setup } = require('../lib/setup');
5
+ const { serve } = require('../lib/serve');
6
+ const { classify } = require('../lib/classify');
7
+ const { seed } = require('../lib/seed');
8
+ const { getPythonVersion } = require('../lib/utils');
9
+ const path = require('path');
10
+
11
+ const VERSION = require('../package.json').version;
12
+
13
+ const HELP = `
14
+ FlakeIQ v${VERSION} — Test flake tracking + LLM failure classification
15
+
16
+ Usage:
17
+ npx flakeiq <command> [options]
18
+
19
+ Commands:
20
+ serve Start the dashboard server
21
+ classify Classify test failures from a JSONL file
22
+ seed Generate synthetic seed data
23
+ setup (Re)run Python environment setup
24
+ status Show FlakeIQ status and configuration
25
+ reporter Show reporter setup instructions
26
+ help Show this help message
27
+
28
+ Examples:
29
+ npx flakeiq serve Start dashboard at localhost:8080
30
+ npx flakeiq serve --port 3000 Start on port 3000
31
+ npx flakeiq serve --seed Dashboard with demo data
32
+ npx flakeiq serve --open Auto-open browser
33
+ npx flakeiq classify results.jsonl Classify failures from JSONL
34
+ npx flakeiq seed Create seed database
35
+ npx flakeiq status Check environment
36
+ `;
37
+
38
+ function parseArgs(args) {
39
+ const parsed = { _: [] };
40
+ for (let i = 0; i < args.length; i++) {
41
+ const arg = args[i];
42
+ if (arg.startsWith('--')) {
43
+ const key = arg.slice(2);
44
+ const next = args[i + 1];
45
+ if (next && !next.startsWith('--')) {
46
+ parsed[key] = next;
47
+ i++;
48
+ } else {
49
+ parsed[key] = true;
50
+ }
51
+ } else {
52
+ parsed._.push(arg);
53
+ }
54
+ }
55
+ return parsed;
56
+ }
57
+
58
+ function ensurePython() {
59
+ if (!isVenvReady()) {
60
+ console.log('Python environment not set up. Running setup...');
61
+ setup();
62
+ if (!isVenvReady()) {
63
+ console.error('\nCannot continue without Python. Install Python 3.11+ and retry.');
64
+ process.exit(1);
65
+ }
66
+ }
67
+ }
68
+
69
+ function showStatus() {
70
+ const config = getConfig();
71
+ const pythonVer = getPythonVersion(config.venvPython);
72
+ const sysPython = getPythonVersion('python3') || getPythonVersion('python');
73
+
74
+ console.log('');
75
+ console.log(` FlakeIQ v${VERSION}`);
76
+ console.log(' ─────────────────────────────────');
77
+ console.log(` Node.js: ${process.version}`);
78
+ console.log(` Python (system): ${sysPython ? sysPython.raw : 'NOT FOUND'}`);
79
+ console.log(` Python (venv): ${pythonVer ? pythonVer.raw : 'NOT SET UP'}`);
80
+ console.log(` Venv ready: ${isVenvReady() ? 'Yes' : 'No'}`);
81
+ console.log(` Database: ${config.dbPath}`);
82
+ console.log(` Data dir: ${config.dataDir}`);
83
+ console.log(` Ollama URL: ${config.ollamaUrl}`);
84
+ console.log(` Ollama model: ${config.ollamaModel}`);
85
+ console.log(` Default port: ${config.port}`);
86
+ console.log(` Reporter: ${config.reporterPath}`);
87
+ console.log('');
88
+ }
89
+
90
+ function showReporterSetup() {
91
+ console.log('');
92
+ console.log(' Reporter Setup');
93
+ console.log(' ─────────────────────────────────');
94
+ console.log('');
95
+ console.log(' 1. Add to your playwright.config.ts:');
96
+ console.log('');
97
+ console.log(' export default {');
98
+ console.log(" reporter: [['html'], ['flakeiq/reporter']],");
99
+ console.log(' // ...');
100
+ console.log(' }');
101
+ console.log('');
102
+ console.log(' 2. Run your tests:');
103
+ console.log(' npx playwright test');
104
+ console.log('');
105
+ console.log(' 3. View results:');
106
+ console.log(' npx flakeiq serve');
107
+ console.log('');
108
+ console.log(' Output: flake-results.jsonl (auto-generated)');
109
+ console.log('');
110
+ }
111
+
112
+ async function main() {
113
+ const args = parseArgs(process.argv.slice(2));
114
+ const command = args._[0] || 'help';
115
+
116
+ switch (command) {
117
+ case 'serve':
118
+ ensurePython();
119
+ await serve({
120
+ port: args.port ? parseInt(args.port) : undefined,
121
+ host: args.host,
122
+ seed: args.seed || false,
123
+ open: args.open !== undefined ? args.open : true,
124
+ db: args.db,
125
+ });
126
+ break;
127
+
128
+ case 'classify':
129
+ ensurePython();
130
+ classify(args._[1], {
131
+ db: args.db,
132
+ ollamaUrl: args['ollama-url'],
133
+ model: args.model,
134
+ });
135
+ break;
136
+
137
+ case 'seed':
138
+ ensurePython();
139
+ seed(args._[1]);
140
+ break;
141
+
142
+ case 'setup':
143
+ setup();
144
+ break;
145
+
146
+ case 'status':
147
+ showStatus();
148
+ break;
149
+
150
+ case 'reporter':
151
+ case 'init':
152
+ showReporterSetup();
153
+ break;
154
+
155
+ case 'help':
156
+ case '--help':
157
+ case '-h':
158
+ console.log(HELP);
159
+ break;
160
+
161
+ case '--version':
162
+ case '-v':
163
+ console.log(`flakeiq v${VERSION}`);
164
+ break;
165
+
166
+ default:
167
+ console.error(` Unknown command: ${command}`);
168
+ console.log(' Run "npx flakeiq help" for usage.');
169
+ process.exit(1);
170
+ }
171
+ }
172
+
173
+ main();
@@ -0,0 +1,38 @@
1
+ const { spawn } = require('child_process');
2
+ const path = require('path');
3
+ const { getConfig } = require('./config');
4
+
5
+ function classify(jsonlPath, options = {}) {
6
+ const config = getConfig();
7
+ const python = config.venvPython;
8
+ const classifyScript = path.join(config.pythonDir, 'classify.py');
9
+
10
+ const args = [classifyScript];
11
+ if (jsonlPath) args.push(jsonlPath);
12
+ if (options.db) args.push('--db', options.db);
13
+ if (options.ollamaUrl) args.push('--ollama-url', options.ollamaUrl);
14
+ if (options.model) args.push('--model', options.model);
15
+
16
+ const child = spawn(python, args, {
17
+ stdio: 'inherit',
18
+ env: {
19
+ ...process.env,
20
+ FLAKE_DB: options.db || config.dbPath,
21
+ OLLAMA_URL: options.ollamaUrl || config.ollamaUrl,
22
+ OLLAMA_MODEL: options.model || config.ollamaModel,
23
+ },
24
+ });
25
+
26
+ child.on('error', (err) => {
27
+ if (err.code === 'ENOENT') {
28
+ console.error('Python not found. Run "npx flakeiq setup" or install Python 3.11+.');
29
+ } else {
30
+ console.error('Classify error:', err.message);
31
+ }
32
+ process.exit(1);
33
+ });
34
+
35
+ child.on('exit', (code) => { process.exit(code || 0); });
36
+ }
37
+
38
+ module.exports = { classify };
package/lib/config.js ADDED
@@ -0,0 +1,51 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+
4
+ const PACKAGE_DIR = path.resolve(__dirname, '..');
5
+ const PYTHON_DIR = path.join(PACKAGE_DIR, 'python');
6
+ const REPORTER_PATH = path.join(PACKAGE_DIR, 'reporter', 'flake-reporter.js');
7
+
8
+ function getVenvDir() {
9
+ return path.join(PACKAGE_DIR, '.flakeiq');
10
+ }
11
+
12
+ function getVenvPython() {
13
+ const venvDir = getVenvDir();
14
+ if (process.platform === 'win32') {
15
+ return path.join(venvDir, 'Scripts', 'python.exe');
16
+ }
17
+ return path.join(venvDir, 'bin', 'python3');
18
+ }
19
+
20
+ function getDataDir() {
21
+ const dir = process.env.FLAKE_DATA_DIR || path.join(process.cwd(), 'flakeiq-data');
22
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
23
+ return dir;
24
+ }
25
+
26
+ function getDbPath() {
27
+ if (process.env.FLAKE_DB) return process.env.FLAKE_DB;
28
+ return path.join(getDataDir(), 'flake.db');
29
+ }
30
+
31
+ function getConfig() {
32
+ return {
33
+ packageDir: PACKAGE_DIR,
34
+ pythonDir: PYTHON_DIR,
35
+ reporterPath: REPORTER_PATH,
36
+ venvDir: getVenvDir(),
37
+ venvPython: getVenvPython(),
38
+ dataDir: getDataDir(),
39
+ dbPath: getDbPath(),
40
+ port: parseInt(process.env.FLAKE_PORT || '8080', 10),
41
+ host: process.env.FLAKE_HOST || '127.0.0.1',
42
+ ollamaUrl: process.env.OLLAMA_URL || 'http://localhost:11434/api/generate',
43
+ ollamaModel: process.env.OLLAMA_MODEL || 'llama3.2',
44
+ };
45
+ }
46
+
47
+ function isVenvReady() {
48
+ return fs.existsSync(getVenvPython());
49
+ }
50
+
51
+ module.exports = { getVenvDir, getVenvPython, getDataDir, getDbPath, getConfig, isVenvReady, PACKAGE_DIR, PYTHON_DIR, REPORTER_PATH };