blun-king-cli 9.1.278 → 9.1.280
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.
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { randomUUID } = require('node:crypto');
|
|
4
|
-
const { mkdir, writeFile } = require('node:fs/promises');
|
|
4
|
+
const { lstat, mkdir, readdir, unlink, writeFile } = require('node:fs/promises');
|
|
5
5
|
const path = require('node:path');
|
|
6
6
|
|
|
7
7
|
const COMPACTION_HISTORY_DIR = 'conversation-history';
|
|
8
|
+
const COMPACTION_HISTORY_RETENTION_DAYS = 30;
|
|
9
|
+
const MAX_COMPACTION_HISTORY_RETENTION_DAYS = 3650;
|
|
10
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
8
11
|
const INLINE_MEDIA_NOTICE = '[inline media payload omitted; original remains in session wire]';
|
|
9
12
|
|
|
10
13
|
async function archiveCompactionHistory(options = {}) {
|
|
@@ -17,6 +20,11 @@ async function archiveCompactionHistory(options = {}) {
|
|
|
17
20
|
const outputPath = path.join(dir, `compaction-${timestamp}-${id}.md`);
|
|
18
21
|
|
|
19
22
|
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
23
|
+
await sweepCompactionHistory({
|
|
24
|
+
homedir,
|
|
25
|
+
now,
|
|
26
|
+
retentionDays: options.retentionDays,
|
|
27
|
+
});
|
|
20
28
|
await writeFile(outputPath, serializeCompactionHistory(messages, { now }), {
|
|
21
29
|
encoding: 'utf8',
|
|
22
30
|
flag: 'wx',
|
|
@@ -25,6 +33,49 @@ async function archiveCompactionHistory(options = {}) {
|
|
|
25
33
|
return outputPath;
|
|
26
34
|
}
|
|
27
35
|
|
|
36
|
+
async function sweepCompactionHistory(options = {}) {
|
|
37
|
+
const homedir = requiredString(options.homedir, 'homedir');
|
|
38
|
+
const now = finiteNumber(options.now, Date.now());
|
|
39
|
+
const retentionDays = options.retentionDays === undefined
|
|
40
|
+
? resolveCompactionHistoryRetentionDays(process.env.BLUN_COMPACTION_HISTORY_RETENTION_DAYS)
|
|
41
|
+
: resolveCompactionHistoryRetentionDays(options.retentionDays);
|
|
42
|
+
const result = { deleted: 0, failed: 0 };
|
|
43
|
+
if (retentionDays === 0) return result;
|
|
44
|
+
const dir = path.join(homedir, COMPACTION_HISTORY_DIR);
|
|
45
|
+
let entries;
|
|
46
|
+
try {
|
|
47
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (error?.code !== 'ENOENT') result.failed += 1;
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
const cutoff = now - retentionDays * DAY_MS;
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
if (!entry.isFile() || !/^compaction-.*\.md$/u.test(entry.name)) continue;
|
|
55
|
+
const candidate = path.join(dir, entry.name);
|
|
56
|
+
try {
|
|
57
|
+
const info = await lstat(candidate);
|
|
58
|
+
if (!info.isFile() || info.mtimeMs >= cutoff) continue;
|
|
59
|
+
await unlink(candidate);
|
|
60
|
+
result.deleted += 1;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (error?.code !== 'ENOENT') result.failed += 1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function resolveCompactionHistoryRetentionDays(value) {
|
|
69
|
+
if (value === undefined || value === null || value === '') {
|
|
70
|
+
return COMPACTION_HISTORY_RETENTION_DAYS;
|
|
71
|
+
}
|
|
72
|
+
const parsed = Number(value);
|
|
73
|
+
if (!Number.isInteger(parsed) || parsed < 0 || parsed > MAX_COMPACTION_HISTORY_RETENTION_DAYS) {
|
|
74
|
+
return COMPACTION_HISTORY_RETENTION_DAYS;
|
|
75
|
+
}
|
|
76
|
+
return parsed;
|
|
77
|
+
}
|
|
78
|
+
|
|
28
79
|
function serializeCompactionHistory(messages, options = {}) {
|
|
29
80
|
const now = finiteNumber(options.now, Date.now());
|
|
30
81
|
const lines = [
|
|
@@ -106,7 +157,10 @@ function finiteNumber(value, fallback) {
|
|
|
106
157
|
|
|
107
158
|
module.exports = {
|
|
108
159
|
COMPACTION_HISTORY_DIR,
|
|
160
|
+
COMPACTION_HISTORY_RETENTION_DAYS,
|
|
109
161
|
archiveCompactionHistory,
|
|
110
162
|
buildCompactionArchiveNotice,
|
|
163
|
+
resolveCompactionHistoryRetentionDays,
|
|
111
164
|
serializeCompactionHistory,
|
|
165
|
+
sweepCompactionHistory,
|
|
112
166
|
};
|
|
@@ -6,6 +6,8 @@ const path = require('node:path');
|
|
|
6
6
|
|
|
7
7
|
const CANDIDATE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
8
8
|
const SCHEMA_VERSION = 1;
|
|
9
|
+
const TERMINAL_CANDIDATE_MAX = 200;
|
|
10
|
+
const TERMINAL_CANDIDATE_TRIM_TO = 100;
|
|
9
11
|
|
|
10
12
|
const COMMAND_TOOLS = new Set([
|
|
11
13
|
'bash',
|
|
@@ -171,6 +173,35 @@ function readValidatedLearningCandidates(options = {}) {
|
|
|
171
173
|
}
|
|
172
174
|
}
|
|
173
175
|
|
|
176
|
+
function pruneTerminalValidatedLearningCandidates(options = {}) {
|
|
177
|
+
const candidates = Array.isArray(options.candidates)
|
|
178
|
+
? options.candidates
|
|
179
|
+
: readValidatedLearningCandidates(options);
|
|
180
|
+
const terminal = candidates.filter((candidate) => (
|
|
181
|
+
candidate.status === 'recorded' || candidate.status === 'expired'
|
|
182
|
+
));
|
|
183
|
+
if (terminal.length <= TERMINAL_CANDIDATE_MAX) return { deleted: 0, failed: 0 };
|
|
184
|
+
|
|
185
|
+
const victims = terminal.slice(0, terminal.length - TERMINAL_CANDIDATE_TRIM_TO);
|
|
186
|
+
let deleted = 0;
|
|
187
|
+
let failed = 0;
|
|
188
|
+
for (const candidate of victims) {
|
|
189
|
+
if (typeof candidate.filePath !== 'string' || candidate.filePath.length === 0) {
|
|
190
|
+
failed += 1;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
fs.rmSync(candidate.filePath, { force: true });
|
|
195
|
+
fs.rmSync(statusMarker(candidate.filePath, 'recorded'), { force: true });
|
|
196
|
+
fs.rmSync(statusMarker(candidate.filePath, 'expired'), { force: true });
|
|
197
|
+
deleted += 1;
|
|
198
|
+
} catch {
|
|
199
|
+
failed += 1;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return { deleted, failed };
|
|
203
|
+
}
|
|
204
|
+
|
|
174
205
|
function successfulMistakeRecordCandidateIds(history) {
|
|
175
206
|
const calls = new Map();
|
|
176
207
|
const candidateIds = new Set();
|
|
@@ -291,6 +322,10 @@ function syncValidatedLearningCandidate(history, options = {}) {
|
|
|
291
322
|
if (expiredCandidate) {
|
|
292
323
|
candidates = readValidatedLearningCandidates({ homeDir, profile: options.profile });
|
|
293
324
|
}
|
|
325
|
+
const retention = pruneTerminalValidatedLearningCandidates({ candidates });
|
|
326
|
+
if (retention.deleted > 0) {
|
|
327
|
+
candidates = readValidatedLearningCandidates({ homeDir, profile: options.profile });
|
|
328
|
+
}
|
|
294
329
|
if (signal !== null) {
|
|
295
330
|
const duplicate = pendingCandidateForSignal(candidates, signal);
|
|
296
331
|
if (duplicate !== null) {
|
|
@@ -315,6 +350,7 @@ function syncValidatedLearningCandidate(history, options = {}) {
|
|
|
315
350
|
module.exports = {
|
|
316
351
|
detectValidatedLearningSignal,
|
|
317
352
|
normalizeCommand,
|
|
353
|
+
pruneTerminalValidatedLearningCandidates,
|
|
318
354
|
readValidatedLearningCandidates,
|
|
319
355
|
syncValidatedLearningCandidate,
|
|
320
356
|
};
|