@bli-cockpit/cli 0.2.266 → 0.2.267
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.
|
@@ -21,7 +21,7 @@ export async function runCockpitCli(argv, io) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
if (command === "--version" || command === "-V" || command === "version") {
|
|
24
|
-
writeLine(io?.stdout ?? process.stdout, "0.2.
|
|
24
|
+
writeLine(io?.stdout ?? process.stdout, "0.2.267");
|
|
25
25
|
return 0;
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -2,87 +2,32 @@
|
|
|
2
2
|
* Which images this machine has already sent to the describe door (BLI-16977).
|
|
3
3
|
*
|
|
4
4
|
* One small file in the collector state directory. It is how "each distinct image is sent once"
|
|
5
|
-
* survives from one 15-minute sync to the next.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* survives from one 15-minute sync to the next. The file holds keys only, never a description, never
|
|
6
|
+
* a path, never a byte. If it is lost or corrupt the worst case is the images are sent again and the
|
|
7
|
+
* server answers "known" without calling a model.
|
|
8
|
+
*
|
|
9
|
+
* BLI-23328: the BLI Memory daemon writes the same file when it describes an image at turn end, so
|
|
10
|
+
* the format, the keys, the backoff steps and the locked read-merge-write live in telemetry-core
|
|
11
|
+
* (`image-description-store.ts`), shared by both writers. This module keeps the collector's names.
|
|
9
12
|
*/
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
import { readJsonFile, writeJsonFileAtomic } from "./local-state-files.js";
|
|
13
|
-
export const IMAGE_DESCRIPTION_STATE_FILENAME = "image-descriptions.json";
|
|
14
|
-
/** Oldest keys leave first past this many (about 5 MB on disk). */
|
|
15
|
-
export const IMAGE_DESCRIPTION_STATE_MAX_KEYS = 150_000;
|
|
16
|
-
const KEY_CHARS = 32;
|
|
17
|
-
export function imageStateKey(sha256) {
|
|
18
|
-
return sha256.slice(0, KEY_CHARS);
|
|
19
|
-
}
|
|
20
|
-
export function imageDescriptionStatePath(stateDir) {
|
|
21
|
-
return path.join(stateDir, IMAGE_DESCRIPTION_STATE_FILENAME);
|
|
22
|
-
}
|
|
13
|
+
import { IMAGE_DESCRIPTION_STATE_FILENAME, IMAGE_DESCRIPTION_STATE_MAX_KEYS, IMAGE_FAILED_STATE_MAX_KEYS, imageDescriptionStatePath, imageRetryDelayMs, imageStateKey, mergeImageDescriptionStates, readImageDescriptionState, updateImageDescriptionState, } from "@bli-cockpit/telemetry-core";
|
|
14
|
+
export { IMAGE_DESCRIPTION_STATE_FILENAME, IMAGE_DESCRIPTION_STATE_MAX_KEYS, IMAGE_FAILED_STATE_MAX_KEYS, imageDescriptionStatePath, imageRetryDelayMs, imageStateKey, };
|
|
23
15
|
/** The keys already described, oldest first. A missing or unreadable file is an empty list, said out loud when unreadable. */
|
|
24
16
|
export async function readDescribedImageKeys(stateDir) {
|
|
25
|
-
|
|
26
|
-
const raw = (await readJsonFile(imageDescriptionStatePath(stateDir)));
|
|
27
|
-
if (!Array.isArray(raw?.described))
|
|
28
|
-
return [];
|
|
29
|
-
return raw.described.filter((key) => typeof key === "string" && key.length === KEY_CHARS);
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
if (!isMissingFileFailure(error)) {
|
|
33
|
-
console.error("[image-descriptions] state file unreadable, starting from an empty list", JSON.stringify({ reason: "image_state_unreadable" }));
|
|
34
|
-
}
|
|
35
|
-
return [];
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
/** Oldest failures leave first past this many. */
|
|
39
|
-
export const IMAGE_FAILED_STATE_MAX_KEYS = 5_000;
|
|
40
|
-
const HOUR_MS = 3_600_000;
|
|
41
|
-
/** A refusal that repeats for the same bytes (a 4xx): a week, then a month. */
|
|
42
|
-
const PERMANENT_BACKOFF_MS = [7 * 24 * HOUR_MS, 30 * 24 * HOUR_MS];
|
|
43
|
-
/** A failure that may pass (the door or the model was down): 1 h, 6 h, 24 h, then 72 h. */
|
|
44
|
-
const TRANSIENT_BACKOFF_MS = [HOUR_MS, 6 * HOUR_MS, 24 * HOUR_MS, 72 * HOUR_MS];
|
|
45
|
-
/** How long to leave an image alone after its Nth failed attempt. */
|
|
46
|
-
export function imageRetryDelayMs(attempts, permanent) {
|
|
47
|
-
const steps = permanent ? PERMANENT_BACKOFF_MS : TRANSIENT_BACKOFF_MS;
|
|
48
|
-
return steps[Math.min(Math.max(attempts, 1), steps.length) - 1];
|
|
17
|
+
return (await readImageDescriptionState(stateDir)).described;
|
|
49
18
|
}
|
|
50
19
|
/** The failures on record, key to record. A missing or unreadable file is an empty map, said out loud when unreadable. */
|
|
51
20
|
export async function readFailedImages(stateDir) {
|
|
52
|
-
|
|
53
|
-
const raw = (await readJsonFile(imageDescriptionStatePath(stateDir)));
|
|
54
|
-
if (!raw?.failed || typeof raw.failed !== "object" || Array.isArray(raw.failed))
|
|
55
|
-
return {};
|
|
56
|
-
const kept = {};
|
|
57
|
-
for (const [key, value] of Object.entries(raw.failed)) {
|
|
58
|
-
const record = value;
|
|
59
|
-
if (key.length !== KEY_CHARS || !record)
|
|
60
|
-
continue;
|
|
61
|
-
if (typeof record.reason !== "string" || typeof record.attempts !== "number" || typeof record.retry_after !== "number")
|
|
62
|
-
continue;
|
|
63
|
-
kept[key] = { reason: record.reason, attempts: record.attempts, retry_after: record.retry_after };
|
|
64
|
-
}
|
|
65
|
-
return kept;
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
if (!isMissingFileFailure(error)) {
|
|
69
|
-
console.error("[image-descriptions] failed-image list unreadable, starting from an empty list", JSON.stringify({ reason: "image_failed_state_unreadable" }));
|
|
70
|
-
}
|
|
71
|
-
return {};
|
|
72
|
-
}
|
|
21
|
+
return (await readImageDescriptionState(stateDir)).failed;
|
|
73
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Write this sync's view, merged with whatever the file holds now: the memory daemon may have
|
|
25
|
+
* described images since the tick opened its copy, and those entries must survive.
|
|
26
|
+
*/
|
|
74
27
|
export async function writeImageDescriptionState(stateDir, state) {
|
|
75
|
-
|
|
76
|
-
const kept = keys.length > IMAGE_DESCRIPTION_STATE_MAX_KEYS ? keys.slice(keys.length - IMAGE_DESCRIPTION_STATE_MAX_KEYS) : keys;
|
|
77
|
-
const failedEntries = Object.entries(state.failed).sort((a, b) => a[1].retry_after - b[1].retry_after);
|
|
78
|
-
const failed = Object.fromEntries(failedEntries.slice(Math.max(0, failedEntries.length - IMAGE_FAILED_STATE_MAX_KEYS)));
|
|
79
|
-
await writeJsonFileAtomic(imageDescriptionStatePath(stateDir), {
|
|
80
|
-
schema_version: "cockpit-image-descriptions.v1",
|
|
81
|
-
described: kept,
|
|
82
|
-
failed,
|
|
83
|
-
});
|
|
28
|
+
await updateImageDescriptionState(stateDir, (disk) => mergeImageDescriptionStates({ described: [...state.described], failed: { ...state.failed } }, disk));
|
|
84
29
|
}
|
|
85
30
|
/** Writes the described list and keeps whatever failures are on record. */
|
|
86
31
|
export async function writeDescribedImageKeys(stateDir, keys) {
|
|
87
|
-
await writeImageDescriptionState(stateDir, { described: keys, failed:
|
|
32
|
+
await writeImageDescriptionState(stateDir, { described: keys, failed: {} });
|
|
88
33
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bli-cockpit/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.267",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"test": "node dist/cli.js --help && node ../../scripts/assert-public-cli-routing.mjs && node ../../scripts/assert-public-cli-verb-help.mjs && node ../../scripts/assert-public-cli-surface-doors.mjs && node ../../scripts/assert-public-cli-runtime-files.mjs && node ../../scripts/assert-public-cli-exit-contract.mjs && node ../../scripts/assert-public-cli-no-fleet-posts.mjs && node ../../scripts/assert-public-cli-doctor-json.mjs && node ../../scripts/assert-public-package-pack.mjs --workspace=@bli-cockpit/cli"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@bli-cockpit/memory-mcp": "0.1.
|
|
31
|
-
"@bli-cockpit/mcp": "0.1.
|
|
32
|
-
"@bli-cockpit/telemetry-core": "0.1.
|
|
30
|
+
"@bli-cockpit/memory-mcp": "0.1.79",
|
|
31
|
+
"@bli-cockpit/mcp": "0.1.163",
|
|
32
|
+
"@bli-cockpit/telemetry-core": "0.1.86"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|