knodin 0.7.4 → 0.7.6

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 (104) hide show
  1. package/README.md +4 -1
  2. package/dist/bin/cli.js +108 -1859
  3. package/dist/bin/launcher.js +3 -10
  4. package/dist/chunks/chunk-DMQAGX77.js +654 -0
  5. package/dist/chunks/chunk-F4Z3Z766.js +4 -0
  6. package/dist/chunks/chunk-SIJAQVSX.js +3 -0
  7. package/dist/chunks/chunk-X6M4HUUE.js +2 -0
  8. package/dist/chunks/chunk-YPRMY2LP.js +8 -0
  9. package/dist/chunks/pure-compression-cli-4TA2TQD5.js +5 -0
  10. package/dist/chunks/server-7EDF4CBY.js +14 -0
  11. package/dist/chunks/structural-fast-path-KD5KQSPX.js +4 -0
  12. package/dist/src/init-progress-worker.js +2 -106
  13. package/dist/src/repair-progress-worker.js +2 -120
  14. package/docs/INSTALLATION.md +17 -1
  15. package/docs/releases/0.7.6.md +25 -0
  16. package/package.json +1 -110
  17. package/dist/src/agent-integration.js +0 -238
  18. package/dist/src/artifact-refresh.js +0 -81
  19. package/dist/src/cli-args.js +0 -274
  20. package/dist/src/cli-model.js +0 -358
  21. package/dist/src/codeflow-replay.js +0 -80
  22. package/dist/src/compact-structural.js +0 -96
  23. package/dist/src/competitive-cold-mcp.js +0 -40
  24. package/dist/src/competitive-constraints.js +0 -20
  25. package/dist/src/competitive-manifest.js +0 -411
  26. package/dist/src/competitive-measurement.js +0 -183
  27. package/dist/src/competitive-runner.js +0 -487
  28. package/dist/src/competitive-sandbox.js +0 -108
  29. package/dist/src/context-export.js +0 -422
  30. package/dist/src/context.js +0 -102
  31. package/dist/src/diagnostics.js +0 -449
  32. package/dist/src/docs-sections.js +0 -141
  33. package/dist/src/doctor.js +0 -380
  34. package/dist/src/engine/ann-hnsw.js +0 -271
  35. package/dist/src/engine/embeddings.js +0 -193
  36. package/dist/src/engine/file-walker.js +0 -43
  37. package/dist/src/engine/git-history.js +0 -289
  38. package/dist/src/engine/index.js +0 -13377
  39. package/dist/src/engine/perf.js +0 -115
  40. package/dist/src/engine/prune.js +0 -112
  41. package/dist/src/engine/scip-import.js +0 -408
  42. package/dist/src/engine/source-policy.js +0 -69
  43. package/dist/src/engine/sqlite.js +0 -71
  44. package/dist/src/engine/symbol-delete.js +0 -58
  45. package/dist/src/execution-profile.js +0 -203
  46. package/dist/src/failure-diagnosis.js +0 -649
  47. package/dist/src/fleet.js +0 -7
  48. package/dist/src/git-executable.js +0 -31
  49. package/dist/src/graph-query-health.js +0 -115
  50. package/dist/src/hook-manager-integration.js +0 -156
  51. package/dist/src/index-activity.js +0 -125
  52. package/dist/src/init-progress.js +0 -155
  53. package/dist/src/init.js +0 -1254
  54. package/dist/src/lifecycle-health.js +0 -250
  55. package/dist/src/lsp-readonly.js +0 -217
  56. package/dist/src/output-compression.js +0 -629
  57. package/dist/src/output-telemetry.js +0 -363
  58. package/dist/src/pr-triage.js +0 -638
  59. package/dist/src/progressive-evidence.js +0 -473
  60. package/dist/src/pure-compression-cli.js +0 -101
  61. package/dist/src/relationship-adapters.js +0 -370
  62. package/dist/src/release-attestation.js +0 -533
  63. package/dist/src/release-preflight.js +0 -510
  64. package/dist/src/repair-progress.js +0 -262
  65. package/dist/src/repository-init-process.js +0 -173
  66. package/dist/src/repository-management.js +0 -1231
  67. package/dist/src/response-budget.js +0 -194
  68. package/dist/src/server.js +0 -73
  69. package/dist/src/structural-fast-path.js +0 -338
  70. package/dist/src/structural-snapshot.js +0 -33
  71. package/dist/src/system-config.js +0 -615
  72. package/dist/src/terminal-help.js +0 -83
  73. package/dist/src/tools/knodin-tools.js +0 -1529
  74. package/dist/src/update-ceremony.js +0 -158
  75. package/dist/src/update-policy.js +0 -944
  76. package/dist/src/update-trust.js +0 -503
  77. package/dist/src/version.js +0 -13
  78. package/dist/src/visualization.js +0 -162
  79. package/dist/src/wait-for-fresh.js +0 -98
  80. package/dist/src/worktree-lifecycle.js +0 -231
  81. package/docs/CONTAINED-EXECUTION.md +0 -77
  82. package/docs/DIAGNOSTICS.md +0 -45
  83. package/docs/GIT-HISTORY-REVIEW.md +0 -39
  84. package/docs/PROGRESSIVE-EVIDENCE.md +0 -37
  85. package/docs/PT-ACCESS-RECOMMENDATION.md +0 -91
  86. package/docs/RELEASE-0.3-EVIDENCE.md +0 -73
  87. package/docs/SCIP-IMPORT.md +0 -57
  88. package/docs/SIGNED-UPDATES.md +0 -151
  89. package/docs/TELEMETRY.md +0 -46
  90. package/docs/assets/knodin-favicon.svg +0 -4
  91. package/docs/releases/0.3.0.md +0 -46
  92. package/docs/releases/0.4.0.md +0 -68
  93. package/docs/releases/0.4.1.md +0 -28
  94. package/docs/releases/0.4.2.md +0 -27
  95. package/docs/releases/0.4.3.md +0 -23
  96. package/docs/releases/0.5.0.md +0 -29
  97. package/docs/releases/0.5.1.md +0 -17
  98. package/docs/releases/0.6.0.md +0 -18
  99. package/docs/releases/0.7.0.md +0 -24
  100. package/docs/releases/0.7.1.md +0 -21
  101. package/docs/releases/0.7.2.md +0 -21
  102. package/docs/releases/0.7.3.md +0 -23
  103. package/docs/releases/0.7.4.md +0 -17
  104. package/schemas/release-attestation-v1.schema.json +0 -210
@@ -1,363 +0,0 @@
1
- import crypto from "node:crypto";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import { encode } from "gpt-tokenizer/encoding/o200k_base";
5
- export const TELEMETRY_TOKENIZER = "gpt-tokenizer@3.4.0:o200k_base";
6
- export const countOutputTokens = (value) => encode(value).length;
7
- export const DEFAULT_TELEMETRY_RETENTION_DAYS = 30;
8
- const DEFAULT_INPUT = ".knodin-telemetry.jsonl";
9
- function lineCount(value) {
10
- return value.length === 0 ? 0 : value.split(/\r?\n/).length;
11
- }
12
- function repositoryIdentifier(repo) {
13
- return `repo_${crypto.createHash("sha256").update(fs.realpathSync(repo)).digest("hex").slice(0, 16)}`;
14
- }
15
- function privatePath(repoPath, requested, purpose) {
16
- const repo = fs.realpathSync(repoPath);
17
- const target = path.resolve(repo, requested);
18
- if (target !== repo && !target.startsWith(`${repo}${path.sep}`))
19
- throw new Error(`knodin telemetry ${purpose}: path must stay inside the repository`);
20
- const relative = path.relative(repo, target);
21
- let cursor = repo;
22
- for (const segment of relative === "" ? [] : relative.split(path.sep)) {
23
- cursor = path.join(cursor, segment);
24
- let stat;
25
- try {
26
- stat = fs.lstatSync(cursor);
27
- }
28
- catch (error) {
29
- if (error.code === "ENOENT")
30
- break;
31
- throw error;
32
- }
33
- if (stat.isSymbolicLink())
34
- throw new Error(purpose === "persist"
35
- ? "knodin telemetry: refusing symlinked persistence path"
36
- : `knodin telemetry ${purpose}: refusing symlinked path`);
37
- }
38
- return { repo, target };
39
- }
40
- function atomicPrivateWrite(target, value) {
41
- fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
42
- const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;
43
- fs.writeFileSync(temporary, value, { mode: 0o600, flag: "wx" });
44
- fs.renameSync(temporary, target);
45
- }
46
- function retentionCutoff(days, now) {
47
- if (!Number.isInteger(days) || days < 1 || days > 3650)
48
- throw new Error("knodin telemetry: retention days must be an integer from 1 to 3650");
49
- return now.getTime() - days * 86_400_000;
50
- }
51
- function percent(baseline, optimized) {
52
- if (baseline === 0)
53
- return optimized === 0 ? 0 : Number.NEGATIVE_INFINITY;
54
- return Math.round(((baseline - optimized) / baseline) * 10_000) / 100;
55
- }
56
- function baselineFiles(repo, operation, output) {
57
- if (!["query:file_summary", "query:batch_outline"].includes(operation))
58
- return [];
59
- const candidates = operation === "query:file_summary"
60
- ? [output.target]
61
- : Array.isArray(output.results)
62
- ? output.results.map((row) => row.file)
63
- : [];
64
- const root = fs.realpathSync(repo);
65
- const files = new Set();
66
- for (const candidate of candidates) {
67
- if (typeof candidate !== "string" || !candidate)
68
- continue;
69
- const resolved = path.resolve(root, candidate);
70
- let real;
71
- try {
72
- real = fs.realpathSync(resolved);
73
- }
74
- catch {
75
- continue;
76
- }
77
- if (real !== root && !real.startsWith(`${root}${path.sep}`))
78
- continue;
79
- if (fs.statSync(real).isFile())
80
- files.add(real);
81
- }
82
- return [...files].sort();
83
- }
84
- export function measureOutput(options) {
85
- const serialized = JSON.stringify(options.output);
86
- const optimized = {
87
- contract: "actual-operation-response",
88
- bytes: Buffer.byteLength(serialized),
89
- tokens: encode(serialized).length,
90
- lines: lineCount(serialized),
91
- };
92
- const files = baselineFiles(options.repo, options.operation, options.output);
93
- const baselineText = files.length > 0 ? files.map((file) => fs.readFileSync(file, "utf-8")).join("\n") : null;
94
- const baseline = baselineText === null
95
- ? null
96
- : {
97
- contract: "executed-full-file-read",
98
- bytes: Buffer.byteLength(baselineText),
99
- tokens: encode(baselineText).length,
100
- fileCount: files.length,
101
- lines: lineCount(baselineText),
102
- };
103
- const status = typeof options.output.status === "string" ? options.output.status : undefined;
104
- const availability = options.output.availability;
105
- const freshness = options.output.freshness;
106
- const fidelity = options.output.fidelity;
107
- const database = path.join(fs.realpathSync(options.repo), ".knodin", "db.sqlite");
108
- const latencyMs = Math.round((performance.now() - options.startedAt) * 100) / 100;
109
- const outcome = (status !== undefined &&
110
- ["error", "failed", "unavailable", "blocked", "timeout"].includes(status)) ||
111
- availability?.state === "unavailable"
112
- ? "error"
113
- : "success";
114
- let compressionFidelity = null;
115
- if (typeof fidelity === "number")
116
- compressionFidelity = fidelity;
117
- else if (typeof fidelity?.percent === "number")
118
- compressionFidelity = fidelity.percent;
119
- return {
120
- schemaVersion: 2,
121
- at: new Date().toISOString(),
122
- repositoryId: repositoryIdentifier(options.repo),
123
- operation: options.operation,
124
- ...(options.executionProfile ? { executionProfile: options.executionProfile } : {}),
125
- latencyMs,
126
- rssBytes: process.memoryUsage().rss,
127
- temperature: "cold",
128
- outcome,
129
- agentRoundTrips: 1,
130
- indexStorageBytes: fs.existsSync(database) ? fs.statSync(database).size : null,
131
- indexingMs: options.operation.startsWith("index") ? latencyMs : null,
132
- freshnessFailure: options.output.staleness === "stale" ||
133
- (typeof freshness?.state === "string" && freshness.state !== "fresh") ||
134
- availability?.state === "unavailable",
135
- compressionFidelity,
136
- confidence: baseline ? "measured" : "unavailable",
137
- truncated: options.truncated,
138
- detailMode: options.detailMode,
139
- tokenizer: TELEMETRY_TOKENIZER,
140
- schemaTokens: options.schemaTokens,
141
- optimized,
142
- baseline,
143
- savings: baseline
144
- ? {
145
- bytes: baseline.bytes - optimized.bytes,
146
- tokens: baseline.tokens - optimized.tokens,
147
- bytePercent: percent(baseline.bytes, optimized.bytes),
148
- tokenPercent: percent(baseline.tokens, optimized.tokens),
149
- }
150
- : null,
151
- };
152
- }
153
- function percentile(values, fraction) {
154
- if (values.length === 0)
155
- return 0;
156
- const sorted = [...values].sort((left, right) => left - right);
157
- return sorted[Math.min(sorted.length - 1, Math.ceil(sorted.length * fraction) - 1)] ?? 0;
158
- }
159
- function escapeHtml(value) {
160
- return value
161
- .replaceAll("&", "&amp;")
162
- .replaceAll("<", "&lt;")
163
- .replaceAll(">", "&gt;")
164
- .replaceAll('"', "&quot;")
165
- .replaceAll("'", "&#39;");
166
- }
167
- function telemetrySummary(records) {
168
- const measured = records.filter(({ savings }) => savings !== null);
169
- return {
170
- calls: records.length,
171
- successes: records.filter(({ outcome }) => outcome !== "error").length,
172
- errors: records.filter(({ outcome }) => outcome === "error").length,
173
- measuredBaselines: measured.length,
174
- measuredTokensAvoided: measured.reduce((sum, record) => sum + (record.savings?.tokens ?? 0), 0),
175
- latencyP50Ms: percentile(records.map(({ latencyMs }) => latencyMs), 0.5),
176
- latencyP95Ms: percentile(records.map(({ latencyMs }) => latencyMs), 0.95),
177
- freshnessFailures: records.filter(({ freshnessFailure }) => freshnessFailure === true).length,
178
- compressionFidelitySamples: records.filter(({ compressionFidelity }) => typeof compressionFidelity === "number").length,
179
- compressionFidelityP50: percentile(records.flatMap(({ compressionFidelity }) => typeof compressionFidelity === "number" ? [compressionFidelity] : []), 0.5),
180
- indexStorageBytes: Math.max(0, ...records.map(({ indexStorageBytes }) => indexStorageBytes ?? 0)),
181
- indexingMs: records.reduce((sum, record) => sum + (record.indexingMs ?? 0), 0),
182
- };
183
- }
184
- export function writeTelemetryReport(repoPath, records, outputPath = ".knodin/telemetry-report.html") {
185
- const { repo, target: output } = privatePath(repoPath, outputPath, "report");
186
- const groups = new Map();
187
- for (const record of records) {
188
- const group = groups.get(record.operation) ?? [];
189
- group.push(record);
190
- groups.set(record.operation, group);
191
- }
192
- const rows = [...groups]
193
- .sort(([left], [right]) => left.localeCompare(right))
194
- .map(([operation, values]) => {
195
- const measured = values.filter(({ savings }) => savings !== null);
196
- const tokens = measured.reduce((sum, value) => sum + (value.savings?.tokens ?? 0), 0);
197
- return `<tr><td>${escapeHtml(operation)}</td><td>${values.length}</td><td>${values.filter(({ outcome }) => outcome === "error").length}</td><td>${measured.length}</td><td>${tokens}</td><td>${percentile(values.map(({ latencyMs }) => latencyMs), 0.5)}</td><td>${percentile(values.map(({ latencyMs }) => latencyMs), 0.95)}</td></tr>`;
198
- })
199
- .join("");
200
- const summary = telemetrySummary(records);
201
- const timeSeries = new Map();
202
- for (const record of records) {
203
- const day = typeof record.at === "string" ? record.at.slice(0, 10) : "unknown";
204
- const current = timeSeries.get(day) ?? { calls: 0, tokens: 0 };
205
- current.calls += 1;
206
- current.tokens += record.savings?.tokens ?? 0;
207
- timeSeries.set(day, current);
208
- }
209
- const seriesRows = [...timeSeries]
210
- .sort(([left], [right]) => left.localeCompare(right))
211
- .map(([day, value]) => `<tr><td>${escapeHtml(day)}</td><td>${value.calls}</td><td>${value.tokens}</td></tr>`)
212
- .join("");
213
- const repositories = new Map();
214
- for (const record of records)
215
- if (record.repositoryId)
216
- repositories.set(record.repositoryId, (repositories.get(record.repositoryId) ?? 0) + 1);
217
- const repositoryRows = [...repositories]
218
- .sort(([left], [right]) => left.localeCompare(right))
219
- .map(([id, calls]) => `<tr><td>${escapeHtml(id)}</td><td>${calls}</td></tr>`)
220
- .join("");
221
- const html = `<!doctype html><meta charset="utf-8"><meta name="color-scheme" content="light dark"><title>knodin telemetry</title><style>:root{color-scheme:light dark;--line:#d0d7de;--card:#f6f8fa}body{font:14px system-ui;margin:2rem;max-width:76rem}section{background:var(--card);padding:1rem;margin:1rem 0;border-radius:.5rem}table{border-collapse:collapse;width:100%}th,td{padding:.55rem;border-bottom:1px solid var(--line);text-align:left}@media(prefers-color-scheme:dark){:root{--line:#30363d;--card:#161b22}}</style><h1>knodin local telemetry</h1><p>Tokenizer: ${TELEMETRY_TOKENIZER}. Local, opt-in, metadata-only; source, raw output, command text, usernames, and paths are not persisted.</p><section><h2>Summary</h2><p>Total measured tokens avoided: ${summary.measuredTokensAvoided}</p><p>Calls: ${summary.calls}; successes: ${summary.successes}; errors: ${summary.errors}; measured baselines: ${summary.measuredBaselines}</p><p>Latency p50 / p95: ${summary.latencyP50Ms} / ${summary.latencyP95Ms} ms</p><p>Freshness failures: ${summary.freshnessFailures}; compression fidelity samples / p50: ${summary.compressionFidelitySamples} / ${summary.compressionFidelityP50}; indexing time: ${summary.indexingMs} ms; index storage: ${summary.indexStorageBytes} bytes</p></section><section><h2>Operation breakdown</h2><table><thead><tr><th>Operation</th><th>Calls</th><th>Errors</th><th>Measured baselines</th><th>Net tokens saved</th><th>p50 ms</th><th>p95 ms</th></tr></thead><tbody>${rows}</tbody></table></section><section><h2>Time series</h2><table><thead><tr><th>Day</th><th>Calls</th><th>Tokens avoided</th></tr></thead><tbody>${seriesRows}</tbody></table></section><section><h2>Repository breakdown</h2><table><thead><tr><th>Private repository id</th><th>Calls</th></tr></thead><tbody>${repositoryRows}</tbody></table></section><p>Use <code>knodin telemetry export</code> for the machine-readable evidence bundle and <code>knodin telemetry clear</code> to delete persisted records.</p>`;
222
- atomicPrivateWrite(output, html);
223
- return {
224
- outputPath: path.relative(repo, output),
225
- records: records.length,
226
- operations: groups.size,
227
- };
228
- }
229
- function sanitizeTelemetryRecord(record) {
230
- return {
231
- schemaVersion: record.schemaVersion,
232
- at: record.at,
233
- repositoryId: record.repositoryId,
234
- operation: record.operation,
235
- ...(record.executionProfile && /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(record.executionProfile)
236
- ? { executionProfile: record.executionProfile }
237
- : {}),
238
- latencyMs: record.latencyMs,
239
- rssBytes: record.rssBytes,
240
- temperature: record.temperature,
241
- outcome: record.outcome,
242
- agentRoundTrips: record.agentRoundTrips,
243
- indexStorageBytes: record.indexStorageBytes,
244
- indexingMs: record.indexingMs,
245
- freshnessFailure: record.freshnessFailure,
246
- compressionFidelity: record.compressionFidelity,
247
- confidence: record.confidence,
248
- truncated: record.truncated,
249
- detailMode: record.detailMode,
250
- tokenizer: record.tokenizer,
251
- schemaTokens: record.schemaTokens,
252
- optimized: {
253
- contract: "actual-operation-response",
254
- bytes: record.optimized.bytes,
255
- tokens: record.optimized.tokens,
256
- lines: record.optimized.lines,
257
- },
258
- baseline: record.baseline
259
- ? {
260
- contract: "executed-full-file-read",
261
- bytes: record.baseline.bytes,
262
- tokens: record.baseline.tokens,
263
- fileCount: record.baseline.fileCount,
264
- lines: record.baseline.lines,
265
- }
266
- : null,
267
- savings: record.savings
268
- ? {
269
- bytes: record.savings.bytes,
270
- tokens: record.savings.tokens,
271
- bytePercent: record.savings.bytePercent,
272
- tokenPercent: record.savings.tokenPercent,
273
- }
274
- : null,
275
- };
276
- }
277
- export function readTelemetryRecords(repoPath, inputPath = DEFAULT_INPUT, retentionDays, now = new Date()) {
278
- const { target: input } = privatePath(repoPath, inputPath, "read");
279
- if (!fs.existsSync(input))
280
- return [];
281
- const records = fs
282
- .readFileSync(input, "utf-8")
283
- .split(/\r?\n/)
284
- .filter(Boolean)
285
- .map((line, index) => {
286
- try {
287
- return JSON.parse(line);
288
- }
289
- catch {
290
- throw new Error(`knodin telemetry read: malformed JSONL record at line ${index + 1}`);
291
- }
292
- })
293
- .filter((record) => typeof record.operation === "string" &&
294
- record.tokenizer === TELEMETRY_TOKENIZER &&
295
- typeof record.optimized?.tokens === "number")
296
- .map(sanitizeTelemetryRecord);
297
- if (retentionDays === undefined)
298
- return records;
299
- const cutoff = retentionCutoff(retentionDays, now);
300
- return records.filter(({ at }) => typeof at !== "string" || Date.parse(at) >= cutoff);
301
- }
302
- export function appendTelemetryRecord(repoPath, record, retentionDays = DEFAULT_TELEMETRY_RETENTION_DAYS, now = new Date(), inputPath = DEFAULT_INPUT) {
303
- const { target } = privatePath(repoPath, inputPath, "persist");
304
- const records = readTelemetryRecords(repoPath, inputPath);
305
- records.push(sanitizeTelemetryRecord({ ...record, at: record.at ?? now.toISOString() }));
306
- const cutoff = retentionCutoff(retentionDays, now);
307
- const retained = records.filter(({ at }) => typeof at !== "string" || Date.parse(at) >= cutoff);
308
- atomicPrivateWrite(target, retained.length === 0 ? "" : `${retained.map((value) => JSON.stringify(value)).join("\n")}\n`);
309
- return { records: retained.length, retentionDays };
310
- }
311
- export function telemetryStatus(repoPath, inputPath = DEFAULT_INPUT, retentionDays = DEFAULT_TELEMETRY_RETENTION_DAYS, now = new Date()) {
312
- const { repo, target } = privatePath(repoPath, inputPath, "status");
313
- const persisted = fs.existsSync(target);
314
- const records = readTelemetryRecords(repo, inputPath, retentionDays, now);
315
- const dates = records
316
- .map(({ at }) => (typeof at === "string" ? at : null))
317
- .filter((value) => value !== null)
318
- .sort((left, right) => left.localeCompare(right));
319
- return {
320
- schemaVersion: 1,
321
- enabledByDefault: false,
322
- localOnly: true,
323
- persisted,
324
- inputPath: path.relative(repo, target),
325
- records: records.length,
326
- bytes: persisted ? fs.statSync(target).size : 0,
327
- retentionDays,
328
- oldestAt: dates[0] ?? null,
329
- newestAt: dates.at(-1) ?? null,
330
- tokenizer: TELEMETRY_TOKENIZER,
331
- };
332
- }
333
- export function exportTelemetry(repoPath, records, outputPath = ".knodin/telemetry-export.json") {
334
- const { repo, target } = privatePath(repoPath, outputPath, "export");
335
- const bundle = {
336
- schemaVersion: 1,
337
- generatedAt: new Date().toISOString(),
338
- privacy: "local-metadata-only",
339
- tokenizer: TELEMETRY_TOKENIZER,
340
- methodology: {
341
- measured: "actual operation response versus executed repository-contained full-file read",
342
- modeled: null,
343
- unknownBaselines: "null",
344
- },
345
- summary: telemetrySummary(records),
346
- records,
347
- };
348
- atomicPrivateWrite(target, `${JSON.stringify(bundle, null, 2)}\n`);
349
- return {
350
- outputPath: path.relative(repo, target),
351
- records: records.length,
352
- summary: bundle.summary,
353
- };
354
- }
355
- export function clearTelemetry(repoPath, inputPath = DEFAULT_INPUT) {
356
- const { target } = privatePath(repoPath, inputPath, "clear");
357
- if (!fs.existsSync(target))
358
- return { removed: false, records: 0, bytes: 0 };
359
- const records = readTelemetryRecords(repoPath, inputPath).length;
360
- const bytes = fs.statSync(target).size;
361
- fs.unlinkSync(target);
362
- return { removed: true, records, bytes };
363
- }