@bli-cockpit/cli 0.2.269 → 0.2.270
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.270");
|
|
25
25
|
return 0;
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { encodeChunkForStorage } from "./evidence-upload-encoding.js";
|
|
2
2
|
import { fetchWithDeadline, isUploadDeadlineError, uploadDeadlineMs } from "./evidence-upload-deadline.js";
|
|
3
3
|
import { noteSyncProgress } from "./sync-lock.js";
|
|
4
|
-
export async function putSignedChunk(options, signedUrl, bytes, uploadId, index) {
|
|
5
|
-
// Identical manifest +
|
|
6
|
-
const encoded =
|
|
4
|
+
export async function putSignedChunk(options, signedUrl, bytes, uploadId, index, codec = "gzip") {
|
|
5
|
+
// Identical manifest + codec framing to the server chunk route staging write (gzip unless the server listed more).
|
|
6
|
+
const encoded = encodeChunkForStorage(bytes, codec);
|
|
7
7
|
console.error("[evidence-upload] chunk encoded for storage", JSON.stringify({
|
|
8
|
-
upload_id: uploadId, chunk_index: index, chunk_encoding:
|
|
8
|
+
upload_id: uploadId, chunk_index: index, chunk_encoding: codec,
|
|
9
9
|
bytes_before: bytes.length, bytes_after: encoded.length,
|
|
10
10
|
}));
|
|
11
11
|
const deadlineMs = uploadDeadlineMs(encoded.length);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { EVIDENCE_CODEC_PREFERENCE, EVIDENCE_MANIFESTS, compressEvidenceBytes, zstdAvailable, } from "@bli-cockpit/telemetry-core/evidence-storage-encoding";
|
|
2
|
+
/** What this Node can write, most preferred first. Sent on `begin`. */
|
|
3
|
+
export function localEncodings(zstd = zstdAvailable()) {
|
|
4
|
+
return EVIDENCE_CODEC_PREFERENCE.filter(codec => codec !== "zstd" || zstd);
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* zstd when this Node has it and the server lists it, else brotli when listed, else gzip.
|
|
8
|
+
* A reply with no list (an old server) means gzip, which every server has always decoded.
|
|
9
|
+
*/
|
|
10
|
+
export function chooseUploadEncoding(accepted, zstd = zstdAvailable()) {
|
|
11
|
+
if (!accepted || accepted.length === 0)
|
|
12
|
+
return { codec: "gzip", reason: "server_did_not_advertise" };
|
|
13
|
+
if (accepted.includes("zstd") && zstd)
|
|
14
|
+
return { codec: "zstd", reason: "server_accepts_zstd" };
|
|
15
|
+
if (accepted.includes("br")) {
|
|
16
|
+
return { codec: "br", reason: accepted.includes("zstd") ? "node_without_zstd" : "server_accepts_brotli_only" };
|
|
17
|
+
}
|
|
18
|
+
return { codec: "gzip", reason: accepted.includes("zstd") ? "node_without_zstd" : "server_did_not_advertise" };
|
|
19
|
+
}
|
|
20
|
+
/** The bytes the server stages for one chunk: the codec's manifest, then its stream. */
|
|
21
|
+
export function encodeChunkForStorage(bytes, codec) {
|
|
22
|
+
return Buffer.concat([EVIDENCE_MANIFESTS[codec], compressEvidenceBytes(bytes, codec)]);
|
|
23
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { putSignedChunk } from "./evidence-upload-direct.js";
|
|
2
|
-
import {
|
|
2
|
+
import { compressEvidenceBytes } from "@bli-cockpit/telemetry-core/evidence-storage-encoding";
|
|
3
|
+
import { chooseUploadEncoding } from "./evidence-upload-encoding.js";
|
|
3
4
|
import { COMMIT_CRASHED_PLATFORM, RawEvidenceUploadCommitResponseSchema, isPermanentUploadFailure, } from "@bli-cockpit/telemetry-core";
|
|
4
5
|
import { StagedFileError, readVerifiedChunk } from "./evidence-upload-staged.js";
|
|
5
6
|
import { beginOneObject } from "./evidence-upload-terminal.js";
|
|
@@ -41,6 +42,12 @@ async function uploadObjectChunks(options, entry, disposition, chunkSizeBytes, o
|
|
|
41
42
|
if (disposition.disposition === "already_committed" || disposition.commit_ready) {
|
|
42
43
|
return { ok: true, uploadedChunks };
|
|
43
44
|
}
|
|
45
|
+
// BLI-23334: the server listed what it decodes; an older one lists nothing and gets gzip.
|
|
46
|
+
const encoding = chooseUploadEncoding(disposition.accepted_encodings);
|
|
47
|
+
console.error("[evidence-upload] encoding chosen", JSON.stringify({
|
|
48
|
+
upload_id: disposition.upload_id, chunk_encoding: encoding.codec, reason: encoding.reason,
|
|
49
|
+
server_listed: disposition.accepted_encodings?.join(",") ?? "none",
|
|
50
|
+
}));
|
|
44
51
|
for (let index = 0; index < entry.chunkCount; index += 1) {
|
|
45
52
|
if (received.has(index))
|
|
46
53
|
continue;
|
|
@@ -60,7 +67,7 @@ async function uploadObjectChunks(options, entry, disposition, chunkSizeBytes, o
|
|
|
60
67
|
}
|
|
61
68
|
const offer = disposition.signed_uploads?.find(upload => upload.chunk_index === index);
|
|
62
69
|
const direct = offer
|
|
63
|
-
? await putSignedChunk(options, offer.signed_url, chunk, disposition.upload_id, index)
|
|
70
|
+
? await putSignedChunk(options, offer.signed_url, chunk, disposition.upload_id, index, encoding.codec)
|
|
64
71
|
: "refused";
|
|
65
72
|
if (direct === "deadline") {
|
|
66
73
|
// BLI-22801: the chunk route would push the same bytes over the same slow link.
|
|
@@ -75,26 +82,26 @@ async function uploadObjectChunks(options, entry, disposition, chunkSizeBytes, o
|
|
|
75
82
|
uploadedChunks += 1;
|
|
76
83
|
continue;
|
|
77
84
|
}
|
|
78
|
-
const failure = await uploadChunkThroughEndpoint(options, entry, disposition, objectKey, index, chunk, uploadedChunks, Boolean(offer));
|
|
85
|
+
const failure = await uploadChunkThroughEndpoint(options, entry, disposition, objectKey, index, chunk, uploadedChunks, Boolean(offer), encoding.codec);
|
|
79
86
|
if (failure)
|
|
80
87
|
return failure;
|
|
81
88
|
uploadedChunks += 1;
|
|
82
89
|
}
|
|
83
90
|
return { ok: true, uploadedChunks };
|
|
84
91
|
}
|
|
85
|
-
async function uploadChunkThroughEndpoint(options, entry, disposition, objectKey, index, chunk, uploadedChunks, hadSignedOffer) {
|
|
92
|
+
async function uploadChunkThroughEndpoint(options, entry, disposition, objectKey, index, chunk, uploadedChunks, hadSignedOffer, codec) {
|
|
86
93
|
console.error("[evidence-upload] using chunk route", JSON.stringify({
|
|
87
94
|
reason: hadSignedOffer ? "signed_upload_refused_fallback" : "signed_upload_not_offered",
|
|
88
95
|
upload_id: disposition.upload_id, chunk_index: index,
|
|
89
96
|
}));
|
|
90
|
-
const encodedChunk =
|
|
97
|
+
const encodedChunk = compressEvidenceBytes(chunk, codec);
|
|
91
98
|
console.error("[evidence-upload] chunk encoded", JSON.stringify({
|
|
92
|
-
upload_id: disposition.upload_id, chunk_index: index, chunk_encoding:
|
|
99
|
+
upload_id: disposition.upload_id, chunk_index: index, chunk_encoding: codec,
|
|
93
100
|
bytes_before: chunk.length, bytes_after: encodedChunk.length,
|
|
94
101
|
}));
|
|
95
102
|
const chunkResponse = await requestJson(options, "/api/ambient/evidence/upload/chunk", {
|
|
96
103
|
schema_version: "ambient-raw-evidence-upload-chunk.v2",
|
|
97
|
-
encoding:
|
|
104
|
+
encoding: codec,
|
|
98
105
|
generated_at: options.generatedAt,
|
|
99
106
|
provenance: options.provenance,
|
|
100
107
|
upload_id: disposition.upload_id,
|
|
@@ -243,6 +243,7 @@ export function readBeginDispositions(body) {
|
|
|
243
243
|
signed_uploads: entry.signed_uploads,
|
|
244
244
|
reason: entry.reason ?? null,
|
|
245
245
|
raw_evidence_pointer_id: entry.raw_evidence_pointer_id,
|
|
246
|
+
...(entry.accepted_encodings ? { accepted_encodings: entry.accepted_encodings } : {}),
|
|
246
247
|
});
|
|
247
248
|
}
|
|
248
249
|
return map;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
|
+
import { EVIDENCE_CODECS_HEADER } from "@bli-cockpit/telemetry-core";
|
|
3
|
+
import { localEncodings } from "./evidence-upload-encoding.js";
|
|
2
4
|
import { describeError } from "./health-detail.js";
|
|
3
5
|
import { fetchWithDeadline, isUploadDeadlineError, uploadDeadlineMs } from "./evidence-upload-deadline.js";
|
|
4
6
|
import { noteSyncProgress } from "./sync-lock.js";
|
|
@@ -26,7 +28,7 @@ export async function requestJson(options, routePath, body) {
|
|
|
26
28
|
"Authorization": `Bearer ${options.deviceToken}`,
|
|
27
29
|
"Content-Type": "application/json",
|
|
28
30
|
...(routePath.endsWith("/evidence/upload/begin")
|
|
29
|
-
? { "x-tower-evidence-direct": "staged-gzip-v1" } : {}),
|
|
31
|
+
? { "x-tower-evidence-direct": "staged-gzip-v1", [EVIDENCE_CODECS_HEADER]: localEncodings().join(",") } : {}),
|
|
30
32
|
},
|
|
31
33
|
body: serialized,
|
|
32
34
|
}, deadlineMs);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bli-cockpit/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.270",
|
|
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.81",
|
|
31
|
+
"@bli-cockpit/mcp": "0.1.165",
|
|
32
|
+
"@bli-cockpit/telemetry-core": "0.1.88"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|