lynkr 8.0.0 → 9.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.
- package/.lynkr/telemetry.db +0 -0
- package/.lynkr/telemetry.db-shm +0 -0
- package/.lynkr/telemetry.db-wal +0 -0
- package/README.md +196 -322
- package/lynkr-skill.tar.gz +0 -0
- package/package.json +4 -3
- package/src/api/openai-router.js +64 -13
- package/src/api/providers-handler.js +171 -3
- package/src/api/router.js +9 -2
- package/src/clients/circuit-breaker.js +10 -247
- package/src/clients/codex-process.js +342 -0
- package/src/clients/codex-utils.js +143 -0
- package/src/clients/databricks.js +210 -63
- package/src/clients/resilience.js +540 -0
- package/src/clients/retry.js +22 -167
- package/src/clients/standard-tools.js +23 -0
- package/src/config/index.js +77 -0
- package/src/context/compression.js +42 -9
- package/src/context/distill.js +492 -0
- package/src/orchestrator/index.js +48 -8
- package/src/routing/complexity-analyzer.js +258 -5
- package/src/routing/index.js +12 -2
- package/src/routing/latency-tracker.js +148 -0
- package/src/routing/model-tiers.js +2 -0
- package/src/routing/quality-scorer.js +113 -0
- package/src/routing/telemetry.js +464 -0
- package/src/server.js +13 -12
- package/src/tools/code-graph.js +538 -0
- package/src/tools/code-mode.js +304 -0
- package/src/tools/index.js +4 -0
- package/src/tools/lazy-loader.js +18 -0
- package/src/tools/mcp-remote.js +7 -0
- package/src/tools/smart-selection.js +11 -0
- package/src/tools/tinyfish.js +358 -0
- package/src/tools/truncate.js +1 -0
- package/src/utils/payload.js +206 -0
- package/src/utils/perf-timer.js +80 -0
- package/.github/FUNDING.yml +0 -15
- package/.github/workflows/README.md +0 -215
- package/.github/workflows/ci.yml +0 -69
- package/.github/workflows/index.yml +0 -62
- package/.github/workflows/web-tools-tests.yml +0 -56
- package/CITATIONS.bib +0 -6
- package/DEPLOYMENT.md +0 -1001
- package/LYNKR-TUI-PLAN.md +0 -984
- package/PERFORMANCE-REPORT.md +0 -866
- package/PLAN-per-client-model-routing.md +0 -252
- package/docs/42642f749da6234f41b6b425c3bb07c9.txt +0 -1
- package/docs/BingSiteAuth.xml +0 -4
- package/docs/docs-style.css +0 -478
- package/docs/docs.html +0 -198
- package/docs/google5be250e608e6da39.html +0 -1
- package/docs/index.html +0 -577
- package/docs/index.md +0 -584
- package/docs/robots.txt +0 -4
- package/docs/sitemap.xml +0 -44
- package/docs/style.css +0 -1223
- package/docs/toon-integration-spec.md +0 -130
- package/documentation/README.md +0 -101
- package/documentation/api.md +0 -806
- package/documentation/claude-code-cli.md +0 -679
- package/documentation/codex-cli.md +0 -397
- package/documentation/contributing.md +0 -571
- package/documentation/cursor-integration.md +0 -734
- package/documentation/docker.md +0 -874
- package/documentation/embeddings.md +0 -762
- package/documentation/faq.md +0 -713
- package/documentation/features.md +0 -403
- package/documentation/headroom.md +0 -519
- package/documentation/installation.md +0 -758
- package/documentation/memory-system.md +0 -476
- package/documentation/production.md +0 -636
- package/documentation/providers.md +0 -1009
- package/documentation/routing.md +0 -476
- package/documentation/testing.md +0 -629
- package/documentation/token-optimization.md +0 -325
- package/documentation/tools.md +0 -697
- package/documentation/troubleshooting.md +0 -969
- package/final-test.js +0 -33
- package/headroom-sidecar/config.py +0 -93
- package/headroom-sidecar/requirements.txt +0 -14
- package/headroom-sidecar/server.py +0 -451
- package/monitor-agents.sh +0 -31
- package/scripts/audit-log-reader.js +0 -399
- package/scripts/compact-dictionary.js +0 -204
- package/scripts/test-deduplication.js +0 -448
- package/src/db/database.sqlite +0 -0
- package/te +0 -11622
- package/test/README.md +0 -212
- package/test/azure-openai-config.test.js +0 -213
- package/test/azure-openai-error-resilience.test.js +0 -238
- package/test/azure-openai-format-conversion.test.js +0 -354
- package/test/azure-openai-integration.test.js +0 -287
- package/test/azure-openai-routing.test.js +0 -175
- package/test/azure-openai-streaming.test.js +0 -171
- package/test/bedrock-integration.test.js +0 -457
- package/test/comprehensive-test-suite.js +0 -928
- package/test/config-validation.test.js +0 -207
- package/test/cursor-integration.test.js +0 -484
- package/test/format-conversion.test.js +0 -578
- package/test/hybrid-routing-integration.test.js +0 -269
- package/test/hybrid-routing-performance.test.js +0 -428
- package/test/llamacpp-integration.test.js +0 -882
- package/test/lmstudio-integration.test.js +0 -347
- package/test/memory/extractor.test.js +0 -398
- package/test/memory/retriever.test.js +0 -613
- package/test/memory/retriever.test.js.bak +0 -585
- package/test/memory/search.test.js +0 -537
- package/test/memory/search.test.js.bak +0 -389
- package/test/memory/store.test.js +0 -344
- package/test/memory/store.test.js.bak +0 -312
- package/test/memory/surprise.test.js +0 -300
- package/test/memory-performance.test.js +0 -472
- package/test/openai-integration.test.js +0 -683
- package/test/openrouter-error-resilience.test.js +0 -418
- package/test/passthrough-mode.test.js +0 -385
- package/test/performance-benchmark.js +0 -351
- package/test/performance-tests.js +0 -528
- package/test/routing.test.js +0 -225
- package/test/toon-compression.test.js +0 -131
- package/test/web-tools.test.js +0 -329
- package/test-agents-simple.js +0 -43
- package/test-cli-connection.sh +0 -33
- package/test-learning-unit.js +0 -126
- package/test-learning.js +0 -112
- package/test-parallel-agents.sh +0 -124
- package/test-parallel-direct.js +0 -155
- 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();
|