lynkr 7.2.5 → 8.0.1

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 (124) hide show
  1. package/README.md +3 -3
  2. package/config/model-tiers.json +89 -0
  3. package/install.sh +6 -1
  4. package/package.json +4 -2
  5. package/scripts/setup.js +0 -1
  6. package/src/agents/executor.js +14 -6
  7. package/src/api/middleware/session.js +15 -2
  8. package/src/api/openai-router.js +162 -37
  9. package/src/api/providers-handler.js +15 -1
  10. package/src/api/router.js +107 -2
  11. package/src/budget/index.js +4 -3
  12. package/src/clients/databricks.js +431 -234
  13. package/src/clients/gpt-utils.js +181 -0
  14. package/src/clients/ollama-utils.js +66 -140
  15. package/src/clients/routing.js +0 -1
  16. package/src/clients/standard-tools.js +99 -3
  17. package/src/config/index.js +133 -35
  18. package/src/context/toon.js +173 -0
  19. package/src/logger/index.js +23 -0
  20. package/src/orchestrator/index.js +688 -213
  21. package/src/routing/agentic-detector.js +320 -0
  22. package/src/routing/complexity-analyzer.js +202 -2
  23. package/src/routing/cost-optimizer.js +305 -0
  24. package/src/routing/index.js +168 -159
  25. package/src/routing/model-tiers.js +365 -0
  26. package/src/server.js +4 -14
  27. package/src/sessions/cleanup.js +3 -3
  28. package/src/sessions/record.js +10 -1
  29. package/src/sessions/store.js +7 -2
  30. package/src/tools/agent-task.js +48 -1
  31. package/src/tools/index.js +19 -2
  32. package/src/tools/lazy-loader.js +7 -0
  33. package/src/tools/tinyfish.js +358 -0
  34. package/src/tools/truncate.js +1 -0
  35. package/.github/FUNDING.yml +0 -15
  36. package/.github/workflows/README.md +0 -215
  37. package/.github/workflows/ci.yml +0 -69
  38. package/.github/workflows/index.yml +0 -62
  39. package/.github/workflows/web-tools-tests.yml +0 -56
  40. package/CITATIONS.bib +0 -6
  41. package/CLAWROUTER_ROUTING_PLAN.md +0 -910
  42. package/DEPLOYMENT.md +0 -1001
  43. package/LYNKR-TUI-PLAN.md +0 -984
  44. package/PERFORMANCE-REPORT.md +0 -866
  45. package/PLAN-per-client-model-routing.md +0 -252
  46. package/ROUTER_COMPARISON.md +0 -173
  47. package/TIER_ROUTING_PLAN.md +0 -771
  48. package/docs/42642f749da6234f41b6b425c3bb07c9.txt +0 -1
  49. package/docs/BingSiteAuth.xml +0 -4
  50. package/docs/docs-style.css +0 -478
  51. package/docs/docs.html +0 -197
  52. package/docs/google5be250e608e6da39.html +0 -1
  53. package/docs/index.html +0 -577
  54. package/docs/index.md +0 -577
  55. package/docs/robots.txt +0 -4
  56. package/docs/sitemap.xml +0 -44
  57. package/docs/style.css +0 -1223
  58. package/documentation/README.md +0 -100
  59. package/documentation/api.md +0 -806
  60. package/documentation/claude-code-cli.md +0 -672
  61. package/documentation/codex-cli.md +0 -397
  62. package/documentation/contributing.md +0 -571
  63. package/documentation/cursor-integration.md +0 -731
  64. package/documentation/docker.md +0 -867
  65. package/documentation/embeddings.md +0 -760
  66. package/documentation/faq.md +0 -659
  67. package/documentation/features.md +0 -396
  68. package/documentation/headroom.md +0 -519
  69. package/documentation/installation.md +0 -706
  70. package/documentation/memory-system.md +0 -476
  71. package/documentation/production.md +0 -601
  72. package/documentation/providers.md +0 -906
  73. package/documentation/testing.md +0 -629
  74. package/documentation/token-optimization.md +0 -323
  75. package/documentation/tools.md +0 -697
  76. package/documentation/troubleshooting.md +0 -893
  77. package/final-test.js +0 -33
  78. package/headroom-sidecar/config.py +0 -93
  79. package/headroom-sidecar/requirements.txt +0 -14
  80. package/headroom-sidecar/server.py +0 -451
  81. package/monitor-agents.sh +0 -31
  82. package/scripts/audit-log-reader.js +0 -399
  83. package/scripts/compact-dictionary.js +0 -204
  84. package/scripts/test-deduplication.js +0 -448
  85. package/src/db/database.sqlite +0 -0
  86. package/test/README.md +0 -212
  87. package/test/azure-openai-config.test.js +0 -204
  88. package/test/azure-openai-error-resilience.test.js +0 -238
  89. package/test/azure-openai-format-conversion.test.js +0 -354
  90. package/test/azure-openai-integration.test.js +0 -281
  91. package/test/azure-openai-routing.test.js +0 -177
  92. package/test/azure-openai-streaming.test.js +0 -171
  93. package/test/bedrock-integration.test.js +0 -471
  94. package/test/comprehensive-test-suite.js +0 -928
  95. package/test/config-validation.test.js +0 -207
  96. package/test/cursor-integration.test.js +0 -484
  97. package/test/format-conversion.test.js +0 -578
  98. package/test/hybrid-routing-integration.test.js +0 -254
  99. package/test/hybrid-routing-performance.test.js +0 -418
  100. package/test/llamacpp-integration.test.js +0 -863
  101. package/test/lmstudio-integration.test.js +0 -335
  102. package/test/memory/extractor.test.js +0 -398
  103. package/test/memory/retriever.test.js +0 -613
  104. package/test/memory/retriever.test.js.bak +0 -585
  105. package/test/memory/search.test.js +0 -537
  106. package/test/memory/search.test.js.bak +0 -389
  107. package/test/memory/store.test.js +0 -344
  108. package/test/memory/store.test.js.bak +0 -312
  109. package/test/memory/surprise.test.js +0 -300
  110. package/test/memory-performance.test.js +0 -472
  111. package/test/openai-integration.test.js +0 -686
  112. package/test/openrouter-error-resilience.test.js +0 -418
  113. package/test/passthrough-mode.test.js +0 -385
  114. package/test/performance-benchmark.js +0 -351
  115. package/test/performance-tests.js +0 -528
  116. package/test/routing.test.js +0 -219
  117. package/test/web-tools.test.js +0 -329
  118. package/test-agents-simple.js +0 -43
  119. package/test-cli-connection.sh +0 -33
  120. package/test-learning-unit.js +0 -126
  121. package/test-learning.js +0 -112
  122. package/test-parallel-agents.sh +0 -124
  123. package/test-parallel-direct.js +0 -155
  124. package/test-subagents.sh +0 -117
@@ -1,399 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * LLM Audit Log Reader
5
- *
6
- * Utility to read and reconstruct audit log entries from deduplicated logs.
7
- * Resolves hash references from the dictionary file and outputs full content.
8
- *
9
- * Usage:
10
- * node scripts/audit-log-reader.js [options]
11
- *
12
- * Options:
13
- * --log-file <path> Path to audit log file (default: logs/llm-audit.log)
14
- * --dict-file <path> Path to dictionary file (default: logs/llm-audit-dictionary.jsonl)
15
- * --full Output full restored entries (resolve all references)
16
- * --filter <type=value> Filter by field (e.g., type=llm_request, provider=anthropic)
17
- * --correlation-id <id> Filter by correlation ID
18
- * --last <n> Show only last N entries
19
- * --stats Show deduplication statistics
20
- * --verify Verify all references can be resolved
21
- * --help Show this help message
22
- *
23
- * Examples:
24
- * # Show all entries with full content
25
- * node scripts/audit-log-reader.js --full
26
- *
27
- * # Show only requests
28
- * node scripts/audit-log-reader.js --filter type=llm_request
29
- *
30
- * # Show last 5 entries
31
- * node scripts/audit-log-reader.js --last 5 --full
32
- *
33
- * # Show deduplication statistics
34
- * node scripts/audit-log-reader.js --stats
35
- *
36
- * # Verify all references resolve
37
- * node scripts/audit-log-reader.js --verify
38
- */
39
-
40
- const fs = require("fs");
41
- const path = require("path");
42
- const readline = require("readline");
43
- const { ContentDeduplicator } = require("../src/logger/deduplicator");
44
-
45
- // Default file paths
46
- const DEFAULT_LOG_FILE = path.join(process.cwd(), "logs", "llm-audit.log");
47
- const DEFAULT_DICT_FILE = path.join(process.cwd(), "logs", "llm-audit-dictionary.jsonl");
48
-
49
- /**
50
- * Parse command line arguments
51
- */
52
- function parseArgs() {
53
- const args = process.argv.slice(2);
54
- const options = {
55
- logFile: DEFAULT_LOG_FILE,
56
- dictFile: DEFAULT_DICT_FILE,
57
- full: false,
58
- filter: null,
59
- correlationId: null,
60
- last: null,
61
- stats: false,
62
- verify: false,
63
- help: false,
64
- };
65
-
66
- for (let i = 0; i < args.length; i++) {
67
- const arg = args[i];
68
- switch (arg) {
69
- case "--log-file":
70
- options.logFile = args[++i];
71
- break;
72
- case "--dict-file":
73
- options.dictFile = args[++i];
74
- break;
75
- case "--full":
76
- options.full = true;
77
- break;
78
- case "--filter":
79
- const filterArg = args[++i];
80
- const [key, value] = filterArg.split("=");
81
- options.filter = { key, value };
82
- break;
83
- case "--correlation-id":
84
- options.correlationId = args[++i];
85
- break;
86
- case "--last":
87
- options.last = Number.parseInt(args[++i], 10);
88
- break;
89
- case "--stats":
90
- options.stats = true;
91
- break;
92
- case "--verify":
93
- options.verify = true;
94
- break;
95
- case "--help":
96
- options.help = true;
97
- break;
98
- default:
99
- console.error(`Unknown option: ${arg}`);
100
- process.exit(1);
101
- }
102
- }
103
-
104
- return options;
105
- }
106
-
107
- /**
108
- * Show help message
109
- */
110
- function showHelp() {
111
- const helpText = `
112
- LLM Audit Log Reader
113
-
114
- Usage: node scripts/audit-log-reader.js [options]
115
-
116
- Options:
117
- --log-file <path> Path to audit log file (default: logs/llm-audit.log)
118
- --dict-file <path> Path to dictionary file (default: logs/llm-audit-dictionary.jsonl)
119
- --full Output full restored entries (resolve all references)
120
- --filter <type=value> Filter by field (e.g., type=llm_request, provider=anthropic)
121
- --correlation-id <id> Filter by correlation ID
122
- --last <n> Show only last N entries
123
- --stats Show deduplication statistics
124
- --verify Verify all references can be resolved
125
- --help Show this help message
126
-
127
- Examples:
128
- # Show all entries with full content
129
- node scripts/audit-log-reader.js --full
130
-
131
- # Show only requests
132
- node scripts/audit-log-reader.js --filter type=llm_request
133
-
134
- # Show last 5 entries
135
- node scripts/audit-log-reader.js --last 5 --full
136
-
137
- # Show deduplication statistics
138
- node scripts/audit-log-reader.js --stats
139
-
140
- # Verify all references resolve
141
- node scripts/audit-log-reader.js --verify
142
- `;
143
- console.log(helpText);
144
- }
145
-
146
- /**
147
- * Read and process log entries
148
- */
149
- async function readLogEntries(options) {
150
- const { logFile, dictFile, full, filter, correlationId, last } = options;
151
-
152
- // Check if log file exists
153
- if (!fs.existsSync(logFile)) {
154
- console.error(`Log file not found: ${logFile}`);
155
- process.exit(1);
156
- }
157
-
158
- // Initialize deduplicator if needed
159
- let deduplicator = null;
160
- if (full && fs.existsSync(dictFile)) {
161
- deduplicator = new ContentDeduplicator(dictFile);
162
- }
163
-
164
- const entries = [];
165
-
166
- // Read log file line by line
167
- const fileStream = fs.createReadStream(logFile);
168
- const rl = readline.createInterface({
169
- input: fileStream,
170
- crlfDelay: Infinity,
171
- });
172
-
173
- for await (const line of rl) {
174
- if (!line.trim()) continue;
175
-
176
- try {
177
- let entry = JSON.parse(line);
178
-
179
- // Apply filters
180
- if (filter && entry[filter.key] !== filter.value) {
181
- continue;
182
- }
183
- if (correlationId && entry.correlationId !== correlationId) {
184
- continue;
185
- }
186
-
187
- // Restore full content if requested
188
- if (full && deduplicator) {
189
- entry = deduplicator.restoreEntry(entry);
190
- }
191
-
192
- entries.push(entry);
193
- } catch (err) {
194
- console.error("Malformed log entry:", err.message);
195
- }
196
- }
197
-
198
- // Apply last N filter
199
- const output = last ? entries.slice(-last) : entries;
200
-
201
- // Output as JSONL
202
- for (const entry of output) {
203
- console.log(JSON.stringify(entry, null, 2));
204
- }
205
-
206
- return entries.length;
207
- }
208
-
209
- /**
210
- * Show deduplication statistics
211
- */
212
- async function showStats(options) {
213
- const { logFile, dictFile } = options;
214
-
215
- if (!fs.existsSync(logFile)) {
216
- console.error(`Log file not found: ${logFile}`);
217
- process.exit(1);
218
- }
219
-
220
- if (!fs.existsSync(dictFile)) {
221
- console.log("No dictionary file found. Deduplication may not be enabled.");
222
- return;
223
- }
224
-
225
- // Get file sizes
226
- const logStats = fs.statSync(logFile);
227
- const dictStats = fs.statSync(dictFile);
228
-
229
- console.log("\n=== LLM Audit Log Deduplication Statistics ===\n");
230
- console.log(`Log file: ${logFile}`);
231
- console.log(` Size: ${formatBytes(logStats.size)}`);
232
- console.log(` Lines: ${await countLines(logFile)}`);
233
- console.log();
234
- console.log(`Dictionary file: ${dictFile}`);
235
- console.log(` Size: ${formatBytes(dictStats.size)}`);
236
- console.log(` Entries: ${await countLines(dictFile)}`);
237
- console.log();
238
- console.log(`Total size: ${formatBytes(logStats.size + dictStats.size)}`);
239
- console.log();
240
-
241
- // Count reference occurrences in log
242
- const refCount = await countReferences(logFile);
243
- console.log(`Reference objects in log: ${refCount}`);
244
- console.log(`Estimated space saved: ~${formatBytes(refCount * 2000)} (assuming ~2KB per deduplicated field)`);
245
- console.log();
246
- }
247
-
248
- /**
249
- * Verify all references can be resolved
250
- */
251
- async function verifyReferences(options) {
252
- const { logFile, dictFile } = options;
253
-
254
- if (!fs.existsSync(logFile)) {
255
- console.error(`Log file not found: ${logFile}`);
256
- process.exit(1);
257
- }
258
-
259
- if (!fs.existsSync(dictFile)) {
260
- console.log("No dictionary file found. Nothing to verify.");
261
- return;
262
- }
263
-
264
- const deduplicator = new ContentDeduplicator(dictFile);
265
- const fileStream = fs.createReadStream(logFile);
266
- const rl = readline.createInterface({
267
- input: fileStream,
268
- crlfDelay: Infinity,
269
- });
270
-
271
- let totalRefs = 0;
272
- let unresolvedRefs = 0;
273
- const unresolvedHashes = new Set();
274
-
275
- console.log("Verifying references...\n");
276
-
277
- for await (const line of rl) {
278
- if (!line.trim()) continue;
279
-
280
- try {
281
- const entry = JSON.parse(line);
282
-
283
- // Check all fields for references
284
- for (const [key, value] of Object.entries(entry)) {
285
- if (typeof value === "object" && value !== null && value.$ref) {
286
- totalRefs++;
287
- const content = deduplicator.getContent(value.$ref);
288
- if (content === null) {
289
- unresolvedRefs++;
290
- unresolvedHashes.add(value.$ref);
291
- console.error(`✗ Unresolved reference: ${value.$ref} in field "${key}"`);
292
- }
293
- }
294
- }
295
- } catch (err) {
296
- console.error("Malformed log entry:", err.message);
297
- }
298
- }
299
-
300
- console.log("\n=== Verification Results ===\n");
301
- console.log(`Total references: ${totalRefs}`);
302
- console.log(`Unresolved references: ${unresolvedRefs}`);
303
- console.log(`Unique unresolved hashes: ${unresolvedHashes.size}`);
304
-
305
- if (unresolvedRefs === 0) {
306
- console.log("\n✓ All references resolved successfully!");
307
- } else {
308
- console.log("\n✗ Some references could not be resolved. Dictionary may be incomplete.");
309
- process.exit(1);
310
- }
311
- }
312
-
313
- /**
314
- * Helper: Format bytes to human-readable string
315
- */
316
- function formatBytes(bytes) {
317
- if (bytes < 1024) return `${bytes} B`;
318
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
319
- return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
320
- }
321
-
322
- /**
323
- * Helper: Count lines in a file
324
- */
325
- async function countLines(filePath) {
326
- const fileStream = fs.createReadStream(filePath);
327
- const rl = readline.createInterface({
328
- input: fileStream,
329
- crlfDelay: Infinity,
330
- });
331
-
332
- let count = 0;
333
- for await (const line of rl) {
334
- if (line.trim()) count++;
335
- }
336
- return count;
337
- }
338
-
339
- /**
340
- * Helper: Count reference objects in log
341
- */
342
- async function countReferences(filePath) {
343
- const fileStream = fs.createReadStream(filePath);
344
- const rl = readline.createInterface({
345
- input: fileStream,
346
- crlfDelay: Infinity,
347
- });
348
-
349
- let count = 0;
350
- for await (const line of rl) {
351
- if (!line.trim()) continue;
352
- try {
353
- const entry = JSON.parse(line);
354
- for (const value of Object.values(entry)) {
355
- if (typeof value === "object" && value !== null && value.$ref) {
356
- count++;
357
- }
358
- }
359
- } catch {
360
- // Skip malformed lines
361
- }
362
- }
363
- return count;
364
- }
365
-
366
- /**
367
- * Main entry point
368
- */
369
- async function main() {
370
- const options = parseArgs();
371
-
372
- if (options.help) {
373
- showHelp();
374
- return;
375
- }
376
-
377
- if (options.stats) {
378
- await showStats(options);
379
- } else if (options.verify) {
380
- await verifyReferences(options);
381
- } else {
382
- const count = await readLogEntries(options);
383
- console.error(`\n(Processed ${count} entries)`);
384
- }
385
- }
386
-
387
- // Run if called directly
388
- if (require.main === module) {
389
- main().catch((err) => {
390
- console.error("Error:", err.message);
391
- process.exit(1);
392
- });
393
- }
394
-
395
- module.exports = {
396
- readLogEntries,
397
- showStats,
398
- verifyReferences,
399
- };
@@ -1,204 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Compact LLM Audit Dictionary
5
- *
6
- * Removes redundant UPDATE entries from the dictionary file, keeping only:
7
- * - One entry per hash with full content
8
- * - Latest metadata (useCount, lastSeen)
9
- *
10
- * Usage:
11
- * node scripts/compact-dictionary.js [options]
12
- *
13
- * Options:
14
- * --dict-path <path> Path to dictionary file (default: logs/llm-audit-dictionary.jsonl)
15
- * --backup Create backup before compacting (default: true)
16
- * --dry-run Show what would be done without making changes
17
- * --help Show this help message
18
- */
19
-
20
- const fs = require('fs');
21
- const path = require('path');
22
- const readline = require('readline');
23
-
24
- // Parse command line arguments
25
- function parseArgs() {
26
- const args = process.argv.slice(2);
27
- const options = {
28
- dictPath: 'logs/llm-audit-dictionary.jsonl',
29
- backup: true,
30
- dryRun: false,
31
- };
32
-
33
- for (let i = 0; i < args.length; i++) {
34
- const arg = args[i];
35
- switch (arg) {
36
- case '--dict-path':
37
- options.dictPath = args[++i];
38
- break;
39
- case '--backup':
40
- options.backup = true;
41
- break;
42
- case '--no-backup':
43
- options.backup = false;
44
- break;
45
- case '--dry-run':
46
- options.dryRun = true;
47
- break;
48
- case '--help':
49
- console.log(`
50
- Compact LLM Audit Dictionary
51
-
52
- Removes redundant UPDATE entries from the dictionary file.
53
-
54
- Usage:
55
- node scripts/compact-dictionary.js [options]
56
-
57
- Options:
58
- --dict-path <path> Path to dictionary file (default: logs/llm-audit-dictionary.jsonl)
59
- --backup Create backup before compacting (default: true)
60
- --no-backup Skip creating backup
61
- --dry-run Show what would be done without making changes
62
- --help Show this help message
63
-
64
- Example:
65
- node scripts/compact-dictionary.js --dict-path logs/llm-audit-dictionary.jsonl --dry-run
66
- `);
67
- process.exit(0);
68
- default:
69
- if (arg.startsWith('--')) {
70
- console.error(`Unknown option: ${arg}`);
71
- process.exit(1);
72
- }
73
- }
74
- }
75
-
76
- return options;
77
- }
78
-
79
- // Read and compact dictionary
80
- async function compactDictionary(dictPath) {
81
- if (!fs.existsSync(dictPath)) {
82
- throw new Error(`Dictionary file not found: ${dictPath}`);
83
- }
84
-
85
- console.log(`Reading dictionary: ${dictPath}`);
86
-
87
- // Map: hash -> entry object
88
- // For each hash, we'll keep the latest metadata merged with content
89
- const entries = new Map();
90
- let totalLines = 0;
91
- let malformedLines = 0;
92
-
93
- // Read all entries
94
- const fileStream = fs.createReadStream(dictPath);
95
- const rl = readline.createInterface({
96
- input: fileStream,
97
- crlfDelay: Infinity,
98
- });
99
-
100
- for await (const line of rl) {
101
- totalLines++;
102
- if (!line.trim()) continue;
103
-
104
- try {
105
- const entry = JSON.parse(line);
106
- if (!entry.hash) {
107
- malformedLines++;
108
- continue;
109
- }
110
-
111
- const hash = entry.hash;
112
-
113
- // Check if we already have an entry for this hash
114
- if (entries.has(hash)) {
115
- const existing = entries.get(hash);
116
-
117
- // Merge: keep content from entry that has it, use latest metadata
118
- const merged = {
119
- hash,
120
- firstSeen: existing.firstSeen || entry.firstSeen,
121
- useCount: entry.useCount || existing.useCount,
122
- lastSeen: entry.lastSeen || existing.lastSeen,
123
- content: existing.content || entry.content,
124
- };
125
-
126
- entries.set(hash, merged);
127
- } else {
128
- // First time seeing this hash
129
- entries.set(hash, entry);
130
- }
131
- } catch (err) {
132
- malformedLines++;
133
- console.warn(`Skipping malformed line ${totalLines}: ${err.message}`);
134
- }
135
- }
136
-
137
- const compactedCount = entries.size;
138
- const removedCount = totalLines - malformedLines - compactedCount;
139
-
140
- return {
141
- entries,
142
- stats: {
143
- totalLines,
144
- malformedLines,
145
- uniqueHashes: compactedCount,
146
- removedEntries: removedCount,
147
- },
148
- };
149
- }
150
-
151
- // Write compacted dictionary
152
- async function writeCompactedDictionary(dictPath, entries, backup = true) {
153
- // Create backup if requested
154
- if (backup) {
155
- const backupPath = `${dictPath}.backup.${Date.now()}`;
156
- console.log(`Creating backup: ${backupPath}`);
157
- fs.copyFileSync(dictPath, backupPath);
158
- }
159
-
160
- // Write compacted entries
161
- console.log(`Writing compacted dictionary: ${dictPath}`);
162
- const lines = Array.from(entries.values()).map((entry) => JSON.stringify(entry));
163
- fs.writeFileSync(dictPath, lines.join('\n') + '\n');
164
- }
165
-
166
- // Main
167
- async function main() {
168
- try {
169
- const options = parseArgs();
170
- const dictPath = path.resolve(options.dictPath);
171
-
172
- console.log('=== LLM Audit Dictionary Compaction ===\n');
173
-
174
- // Read and compact
175
- const { entries, stats } = await compactDictionary(dictPath);
176
-
177
- // Report statistics
178
- console.log('\nCompaction Statistics:');
179
- console.log(` Total lines in dictionary: ${stats.totalLines}`);
180
- console.log(` Malformed lines skipped: ${stats.malformedLines}`);
181
- console.log(` Unique content hashes: ${stats.uniqueHashes}`);
182
- console.log(` Redundant entries removed: ${stats.removedEntries}`);
183
-
184
- const reductionPercent =
185
- stats.totalLines > 0
186
- ? ((stats.removedEntries / stats.totalLines) * 100).toFixed(1)
187
- : 0;
188
- console.log(` Size reduction: ${reductionPercent}%\n`);
189
-
190
- if (options.dryRun) {
191
- console.log('DRY RUN: No changes made to dictionary file.');
192
- console.log(`Would have written ${stats.uniqueHashes} entries.\n`);
193
- } else {
194
- // Write compacted dictionary
195
- await writeCompactedDictionary(dictPath, entries, options.backup);
196
- console.log('✓ Dictionary compaction complete!\n');
197
- }
198
- } catch (err) {
199
- console.error('Error:', err.message);
200
- process.exit(1);
201
- }
202
- }
203
-
204
- main();