@bli-cockpit/cli 0.2.30 → 0.2.32
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/dist/adapters/agent-image-evidence.js +4 -0
- package/dist/adapters/attribution-core.js +12 -0
- package/dist/adapters/car-state.js +12 -1
- package/dist/adapters/claude-attribution.js +81 -7
- package/dist/adapters/codex-attribution.js +37 -3
- package/dist/adapters/raw-evidence-manifest.js +12 -1
- package/dist/adapters/raw-evidence-pack-store.js +28 -3
- package/dist/adapters/raw-evidence-sanitize.js +21 -3
- package/dist/adapters/raw-evidence.js +51 -6
- package/dist/agent-rules.js +34 -3
- package/dist/autostart.js +3 -0
- package/dist/backfill-lock.js +22 -1
- package/dist/commands/backfill.js +41 -7
- package/dist/commands/cli-io.js +3 -0
- package/dist/commands/doctor.js +35 -4
- package/dist/commands/install-receipts.js +43 -6
- package/dist/commands/install-update.js +3 -1
- package/dist/commands/jarvis.js +136 -0
- package/dist/commands/local-args.js +29 -0
- package/dist/commands/local-auth.js +14 -0
- package/dist/commands/local-help.js +15 -0
- package/dist/commands/local.js +24 -1
- package/dist/commands/public-root.js +1 -1
- package/dist/commands/session-sync.js +35 -6
- package/dist/commands/status.js +17 -1
- package/dist/cursors/backfill-cursor.js +23 -2
- package/dist/cursors/raw-evidence-cursor.js +14 -1
- package/dist/discovery-limits.js +12 -1
- package/dist/evidence-upload-client.js +41 -4
- package/dist/health-detail.js +111 -2
- package/dist/local-state.js +98 -11
- package/dist/onboarding-roots.js +3 -0
- package/dist/raw-evidence-attribution-policy.js +7 -0
- package/dist/raw-evidence-gc.js +5 -0
- package/dist/raw-evidence-staging.js +12 -1
- package/dist/repo-identity.js +15 -1
- package/dist/scheduled-self-update.js +14 -1
- package/dist/spool/install-event-outbox.js +11 -1
- package/dist/spool/local-spool.js +3 -0
- package/dist/sync-lock.js +24 -1
- package/dist/upload-agent-artifacts.js +11 -1
- package/dist/upload-envelope.js +30 -3
- package/dist/upload-http.js +10 -0
- package/dist/upload-session-reports.js +36 -3
- package/dist/upload.js +16 -5
- package/package.json +2 -2
package/dist/repo-identity.js
CHANGED
|
@@ -4,6 +4,7 @@ import fs from "node:fs/promises";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
6
|
import { containsPath, isCodexWorktreePath } from "./root-normalization.js";
|
|
7
|
+
import { describeError, isMissingFileFailure } from "./health-detail.js";
|
|
7
8
|
const execFileAsync = promisify(execFile);
|
|
8
9
|
const SKIPPED_DIR_NAMES = new Set([
|
|
9
10
|
".cache",
|
|
@@ -352,7 +353,16 @@ async function resolveBranchFromHead(repoRoot) {
|
|
|
352
353
|
}
|
|
353
354
|
return head ? `detached:${head.slice(0, 12)}` : "unknown";
|
|
354
355
|
}
|
|
355
|
-
catch {
|
|
356
|
+
catch (error) {
|
|
357
|
+
// Not a repo → quiet, that is an ordinary approved folder. A `.git` that
|
|
358
|
+
// exists and will not read → every session from this worktree is labelled
|
|
359
|
+
// branch `unknown` and, until BLI-3238, nothing said why.
|
|
360
|
+
if (!isMissingFileFailure(error)) {
|
|
361
|
+
console.error("[repo-identity] could not read HEAD, branch recorded as unknown", JSON.stringify({
|
|
362
|
+
reason: "git_head_unreadable",
|
|
363
|
+
...describeError(error),
|
|
364
|
+
}));
|
|
365
|
+
}
|
|
356
366
|
return "unknown";
|
|
357
367
|
}
|
|
358
368
|
}
|
|
@@ -377,6 +387,10 @@ export function normalizeGitOrigin(rawOrigin) {
|
|
|
377
387
|
return normalizeOriginParts(url.hostname, url.pathname);
|
|
378
388
|
}
|
|
379
389
|
catch {
|
|
390
|
+
// Deliberately silent (BLI-3238). `new URL` is being used as the test for
|
|
391
|
+
// "is this origin URL-shaped?", and a plain path or an unusual remote form
|
|
392
|
+
// failing to parse IS the answer — the fallback below is the intended
|
|
393
|
+
// normalization for exactly that case, not a degradation.
|
|
380
394
|
return trimmed
|
|
381
395
|
.replace(/\.git$/i, "")
|
|
382
396
|
.replace(/^\/+|\/+$/g, "")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { describeError } from "./health-detail.js";
|
|
3
4
|
const SELF_UPDATE_MIN_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
4
5
|
export const SELF_UPDATE_THROTTLE_MARKER = ".last-self-update-check";
|
|
5
6
|
/**
|
|
@@ -179,7 +180,16 @@ async function probeInstalledCliVersion(exec) {
|
|
|
179
180
|
const version = parsed.dependencies?.["@bli-cockpit/cli"]?.version;
|
|
180
181
|
return typeof version === "string" && version.trim() ? version.trim() : null;
|
|
181
182
|
}
|
|
182
|
-
catch {
|
|
183
|
+
catch (error) {
|
|
184
|
+
// `null` means "installed version unknown", which suppresses the whole
|
|
185
|
+
// self-update decision including the BLI-2678 forced-version floor. A
|
|
186
|
+
// fleet stuck on an old CLI because `npm ls` started printing something
|
|
187
|
+
// unparseable would look exactly like a fleet that is up to date.
|
|
188
|
+
console.error("[self-update] could not read the installed CLI version from npm ls", JSON.stringify({
|
|
189
|
+
reason: "installed_version_unreadable",
|
|
190
|
+
byte_size: result.stdout.length,
|
|
191
|
+
...describeError(error),
|
|
192
|
+
}));
|
|
183
193
|
return null;
|
|
184
194
|
}
|
|
185
195
|
}
|
|
@@ -197,6 +207,9 @@ function parseNpmVersionField(stdout) {
|
|
|
197
207
|
return typeof parsed === "string" && parsed.trim() ? parsed.trim() : null;
|
|
198
208
|
}
|
|
199
209
|
catch {
|
|
210
|
+
// Deliberately silent (BLI-3238), same as doctor.ts's copy: the parse is
|
|
211
|
+
// the test for whether this npm quoted its output, and the unquoted
|
|
212
|
+
// fallback is the intended handling of the other form.
|
|
200
213
|
return trimmed.replace(/^"|"$/gu, "") || null;
|
|
201
214
|
}
|
|
202
215
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { describeError } from "../health-detail.js";
|
|
4
5
|
const OUTBOX_DIRECTORY = "install-events";
|
|
5
6
|
const MAX_PENDING_ENTRIES = 100;
|
|
6
7
|
const MAX_EVENTS_PER_ENTRY = 40;
|
|
@@ -44,12 +45,21 @@ export async function readPendingInstallEventEntries(paths) {
|
|
|
44
45
|
try {
|
|
45
46
|
const parsed = parseEntry(JSON.parse(await fs.readFile(filePath, "utf8")));
|
|
46
47
|
if (!parsed) {
|
|
48
|
+
console.error("[install-outbox] discarding an entry that does not match the schema", JSON.stringify({ reason: "outbox_entry_invalid" }));
|
|
47
49
|
await fs.rm(filePath, { force: true });
|
|
48
50
|
continue;
|
|
49
51
|
}
|
|
50
52
|
entries.push(parsed);
|
|
51
53
|
}
|
|
52
|
-
catch {
|
|
54
|
+
catch (error) {
|
|
55
|
+
// This branch DELETES a queued receipt. Whatever the reason — truncated
|
|
56
|
+
// write, bad JSON, no read permission — the machine loses a health event
|
|
57
|
+
// it was holding for the server, so it says which reason it was before
|
|
58
|
+
// dropping it (BLI-3238).
|
|
59
|
+
console.error("[install-outbox] discarding an unreadable entry", JSON.stringify({
|
|
60
|
+
reason: "outbox_entry_unreadable",
|
|
61
|
+
...describeError(error),
|
|
62
|
+
}));
|
|
53
63
|
await fs.rm(filePath, { force: true }).catch(() => undefined);
|
|
54
64
|
}
|
|
55
65
|
}
|
|
@@ -416,6 +416,9 @@ async function fsyncDirectoryBestEffort(directoryPath) {
|
|
|
416
416
|
catch {
|
|
417
417
|
// Directory fsync is not supported by every host/filesystem (notably some
|
|
418
418
|
// Windows versions). The file itself was fsynced before the atomic rename.
|
|
419
|
+
// Deliberately silent (BLI-3238): on those hosts this fails on every
|
|
420
|
+
// single write, so a line here would be pure noise on exactly the
|
|
421
|
+
// platform the collector most needs readable logs on.
|
|
419
422
|
}
|
|
420
423
|
finally {
|
|
421
424
|
await handle?.close().catch(() => undefined);
|
package/dist/sync-lock.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { describeError } from "./health-detail.js";
|
|
4
5
|
/**
|
|
5
6
|
* Single-flight lock for `cockpit sync`. A launchd timer and a manual sync can
|
|
6
7
|
* fire at the same time and interleave the cursor read-modify-write; this lock
|
|
@@ -71,10 +72,27 @@ async function tryExclusiveCreate(lockPath, token, now) {
|
|
|
71
72
|
await handle.close();
|
|
72
73
|
return true;
|
|
73
74
|
}
|
|
74
|
-
catch {
|
|
75
|
+
catch (error) {
|
|
76
|
+
// EEXIST is the whole point of `wx`: another sync holds the lock, and the
|
|
77
|
+
// caller reports `sync_already_running`. Any OTHER code — no permission on
|
|
78
|
+
// the cursors directory, a full disk, a read-only volume — produces the
|
|
79
|
+
// identical false answer, and every sync on the machine then exits saying
|
|
80
|
+
// another one is running while none ever is (BLI-3238).
|
|
81
|
+
if (!isLockHeldError(error)) {
|
|
82
|
+
console.error("[sync-lock] could not create the lock file; reporting the lock as held", JSON.stringify({
|
|
83
|
+
reason: "sync_lock_create_failed",
|
|
84
|
+
...describeError(error),
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
75
87
|
return false;
|
|
76
88
|
}
|
|
77
89
|
}
|
|
90
|
+
/** `wx` refusing because the lock exists — the ordinary contended case. */
|
|
91
|
+
function isLockHeldError(error) {
|
|
92
|
+
return (typeof error === "object" &&
|
|
93
|
+
error !== null &&
|
|
94
|
+
error.code === "EEXIST");
|
|
95
|
+
}
|
|
78
96
|
async function writeLock(lockPath, token, now) {
|
|
79
97
|
await fs.writeFile(lockPath, serializeLock(token, now), { mode: 0o600 });
|
|
80
98
|
}
|
|
@@ -108,6 +126,11 @@ async function readLock(lockPath) {
|
|
|
108
126
|
};
|
|
109
127
|
}
|
|
110
128
|
catch {
|
|
129
|
+
// Deliberately silent (BLI-3238). This read is the "is anyone holding it?"
|
|
130
|
+
// probe: no lock file, a half-written one, a lock being removed underneath
|
|
131
|
+
// us — every one of those means "not held", which is the answer the caller
|
|
132
|
+
// wants and acts on correctly. Failure IS the result here. The failing
|
|
133
|
+
// ACQUIRE above is the branch that owns the reporting.
|
|
111
134
|
return null;
|
|
112
135
|
}
|
|
113
136
|
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* throwing.
|
|
12
12
|
*/
|
|
13
13
|
import { AgentImageArtifactReportRequestSchema, } from "@bli-cockpit/telemetry-core";
|
|
14
|
+
import { describeError } from "./health-detail.js";
|
|
14
15
|
import { readResponseJson } from "./upload-http.js";
|
|
15
16
|
export async function reportAgentImageArtifacts(options) {
|
|
16
17
|
const artifacts = agentArtifactsFromEvidence(options);
|
|
@@ -60,7 +61,16 @@ export async function reportAgentImageArtifacts(options) {
|
|
|
60
61
|
recorded_count: Number.isFinite(recordedCount) ? recordedCount : 0,
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
|
-
catch {
|
|
64
|
+
catch (error) {
|
|
65
|
+
// The label stays `report_network_error` — it is the wire contract. What
|
|
66
|
+
// it never carried is WHICH network error, and DNS failure, TLS refusal,
|
|
67
|
+
// a proxy reset and a body that would not serialize are four different
|
|
68
|
+
// repairs wearing one name (BLI-3238).
|
|
69
|
+
console.error("[agent-artifacts] report request failed before a status came back", JSON.stringify({
|
|
70
|
+
reason: "report_network_error",
|
|
71
|
+
artifact_count: artifacts.length,
|
|
72
|
+
...describeError(error),
|
|
73
|
+
}));
|
|
64
74
|
return { posted: false, reason: "report_network_error", recorded_count: 0 };
|
|
65
75
|
}
|
|
66
76
|
}
|
package/dist/upload-envelope.js
CHANGED
|
@@ -24,6 +24,7 @@ import { getCollectorRuntimePaths, LOCAL_COLLECTOR_VERSION, readLocalCollectorCo
|
|
|
24
24
|
import { runLocalSourceCollectors } from "./adapters/local-sources.js";
|
|
25
25
|
import { defaultCodexSessionDirs, } from "./adapters/codex-attribution.js";
|
|
26
26
|
import { normalizeDashboardUrl } from "./upload-http.js";
|
|
27
|
+
import { describeError, isMissingFileFailure } from "./health-detail.js";
|
|
27
28
|
export class LocalUploadBlockedError extends Error {
|
|
28
29
|
blocker;
|
|
29
30
|
retry_hint;
|
|
@@ -40,7 +41,16 @@ export async function buildLocalAmbientEnvelope(options = {}) {
|
|
|
40
41
|
const collector = await readPairedCollector(paths);
|
|
41
42
|
const { config, sessionFile, session } = collector;
|
|
42
43
|
const repoRoot = path.resolve(options.repoRoot ?? process.cwd());
|
|
43
|
-
const activeContext = await readLocalWorkContextForRepo(paths, repoRoot).catch(() => {
|
|
44
|
+
const activeContext = await readLocalWorkContextForRepo(paths, repoRoot).catch((error) => {
|
|
45
|
+
// The operator is told "run `cockpit start`", which is right when the file
|
|
46
|
+
// is simply absent and wrong when it exists and will not parse — the same
|
|
47
|
+
// advice, forever, on a machine that has already run it (BLI-3238).
|
|
48
|
+
if (!isMissingFileFailure(error)) {
|
|
49
|
+
console.error("[upload-envelope] work context present but unreadable, reporting it as missing", JSON.stringify({
|
|
50
|
+
reason: "missing_context",
|
|
51
|
+
...describeError(error),
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
44
54
|
throw new LocalUploadBlockedError("missing_context", "Active work context missing. Run `cockpit start --workspace \"$PWD\"` before `cockpit sync`.", "cockpit start --workspace \"$PWD\"");
|
|
45
55
|
});
|
|
46
56
|
const repoLabel = safeRepoLabel(activeContext.repo_label ?? repoRoot);
|
|
@@ -132,10 +142,27 @@ export async function buildLocalAmbientEnvelope(options = {}) {
|
|
|
132
142
|
* with no next step is what turns a five-second fix into a support thread.
|
|
133
143
|
*/
|
|
134
144
|
async function readPairedCollector(paths) {
|
|
135
|
-
const config = await readLocalCollectorConfig(paths).catch(() => {
|
|
145
|
+
const config = await readLocalCollectorConfig(paths).catch((error) => {
|
|
146
|
+
// `not_installed` tells the operator to reinstall the CLI. That is the
|
|
147
|
+
// wrong instruction for a config that exists and is corrupt, and there was
|
|
148
|
+
// no way to tell which one this machine hit.
|
|
149
|
+
if (!isMissingFileFailure(error)) {
|
|
150
|
+
console.error("[upload-envelope] collector config present but unreadable, reporting it as not installed", JSON.stringify({
|
|
151
|
+
reason: "not_installed",
|
|
152
|
+
...describeError(error),
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
136
155
|
throw new LocalUploadBlockedError("not_installed", "Local collector config missing. Install/update the CLI, then run `cockpit do-everything` before `cockpit sync`.", "npm install -g @bli-cockpit/cli@latest && cockpit do-everything");
|
|
137
156
|
});
|
|
138
|
-
const sessionFile = await readLocalCollectorSessionFile(paths).catch(() => {
|
|
157
|
+
const sessionFile = await readLocalCollectorSessionFile(paths).catch((error) => {
|
|
158
|
+
// Same trap on the pairing half: `unpaired` sends the operator to
|
|
159
|
+
// `cockpit login`, which does not fix an unreadable session file.
|
|
160
|
+
if (!isMissingFileFailure(error)) {
|
|
161
|
+
console.error("[upload-envelope] session file present but unreadable, reporting the machine as unpaired", JSON.stringify({
|
|
162
|
+
reason: "unpaired",
|
|
163
|
+
...describeError(error),
|
|
164
|
+
}));
|
|
165
|
+
}
|
|
139
166
|
throw new LocalUploadBlockedError("unpaired", "No paired collector session found. Run `cockpit login` or `cockpit pair` before `cockpit sync`.", "cockpit login");
|
|
140
167
|
});
|
|
141
168
|
const session = await readLocalSessionReference(paths);
|
package/dist/upload-http.js
CHANGED
|
@@ -21,6 +21,16 @@ export async function readResponseJson(response) {
|
|
|
21
21
|
return JSON.parse(text);
|
|
22
22
|
}
|
|
23
23
|
catch {
|
|
24
|
+
// The text itself is never logged: a proxy error page is unbounded and can
|
|
25
|
+
// carry anything. The shape is the useful part — an HTML body under a 200
|
|
26
|
+
// means something in front of the dashboard answered instead of it, which
|
|
27
|
+
// reads downstream as an ordinary upload failure (BLI-3238).
|
|
28
|
+
console.error("[cockpit-http] reply was not JSON", JSON.stringify({
|
|
29
|
+
reason: "response_body_not_json",
|
|
30
|
+
http_status: response.status,
|
|
31
|
+
byte_size: text.length,
|
|
32
|
+
content_type: response.headers.get("content-type") ?? "none",
|
|
33
|
+
}));
|
|
24
34
|
return { message: text };
|
|
25
35
|
}
|
|
26
36
|
}
|
|
@@ -27,6 +27,7 @@ import { getCollectorRuntimePaths, LOCAL_COLLECTOR_VERSION, readLocalCollectorCo
|
|
|
27
27
|
import { readLocalUploadSpoolState, recordPendingSessionReport, recordSessionReportFailure, recordSessionReportSuccess, } from "./spool/local-spool.js";
|
|
28
28
|
import { makeCollectorProvenance, makeUploadWorkContext, safeRepoLabel, } from "./upload-envelope.js";
|
|
29
29
|
import { normalizeDashboardUrl, readResponseJson } from "./upload-http.js";
|
|
30
|
+
import { describeError } from "./health-detail.js";
|
|
30
31
|
/** Attempts per chunk, and the linear 250ms-per-attempt backoff between them. */
|
|
31
32
|
const DEFAULT_REPORT_ATTEMPTS = 3;
|
|
32
33
|
const REPORT_RETRY_BACKOFF_STEP_MS = 250;
|
|
@@ -73,7 +74,19 @@ export async function postCodexSessionReport(options) {
|
|
|
73
74
|
sessions: options.sessions,
|
|
74
75
|
});
|
|
75
76
|
}
|
|
76
|
-
catch {
|
|
77
|
+
catch (error) {
|
|
78
|
+
// `collector_not_ready` covers this whole block — config read, session
|
|
79
|
+
// read, work-context read AND the report request itself. The label is
|
|
80
|
+
// kept (it is what the receipt contract carries) but it is broader than
|
|
81
|
+
// its name suggests: a network failure inside `reportCodexSessionAttributions`
|
|
82
|
+
// also lands here and reads as "this machine is not set up". The detail is
|
|
83
|
+
// now the only way to tell those apart. Flagged, not reclassified —
|
|
84
|
+
// narrowing the label is a contract change (BLI-3238).
|
|
85
|
+
console.error("[session-reports] could not report session attributions", JSON.stringify({
|
|
86
|
+
reason: "collector_not_ready",
|
|
87
|
+
session_count: options.sessions.length,
|
|
88
|
+
...describeError(error),
|
|
89
|
+
}));
|
|
77
90
|
return emptyCodexSessionReportResult("collector_not_ready");
|
|
78
91
|
}
|
|
79
92
|
}
|
|
@@ -125,7 +138,15 @@ export async function flushPendingCodexSessionReports(options) {
|
|
|
125
138
|
readLocalSessionReference(paths),
|
|
126
139
|
]);
|
|
127
140
|
}
|
|
128
|
-
catch {
|
|
141
|
+
catch (error) {
|
|
142
|
+
// This marks every queued report as failed. The count is what makes it
|
|
143
|
+
// worth a line: an unreadable session file here strands N reports at once
|
|
144
|
+
// and the only visible trace is a spool that stops draining (BLI-3238).
|
|
145
|
+
console.error("[session-reports] session file unreadable, failing the pending reports", JSON.stringify({
|
|
146
|
+
reason: "collector_not_ready",
|
|
147
|
+
pending_report_count: state.pending_session_reports.length,
|
|
148
|
+
...describeError(error),
|
|
149
|
+
}));
|
|
129
150
|
return await failPendingSessionReports(paths, state.pending_session_reports, attemptedAt, "collector_not_ready");
|
|
130
151
|
}
|
|
131
152
|
if (session.session_state !== "valid") {
|
|
@@ -311,7 +332,19 @@ async function postCodexSessionAttributionChunk(options) {
|
|
|
311
332
|
}
|
|
312
333
|
}
|
|
313
334
|
}
|
|
314
|
-
catch {
|
|
335
|
+
catch (error) {
|
|
336
|
+
// Logged per attempt, on purpose: the interesting failure is the one
|
|
337
|
+
// that repeats. Three identical ECONNRESETs and one DNS failure followed
|
|
338
|
+
// by two resets are different stories, and `report_network_error` alone
|
|
339
|
+
// tells neither (BLI-3238).
|
|
340
|
+
console.error("[session-reports] report attempt failed before a status came back", JSON.stringify({
|
|
341
|
+
reason: "report_network_error",
|
|
342
|
+
attempt,
|
|
343
|
+
max_attempts: maxAttempts,
|
|
344
|
+
batch_index: options.batchIndex,
|
|
345
|
+
session_count: options.sessions.length,
|
|
346
|
+
...describeError(error),
|
|
347
|
+
}));
|
|
315
348
|
lastStatus = null;
|
|
316
349
|
lastFailureReason = "report_network_error";
|
|
317
350
|
}
|
package/dist/upload.js
CHANGED
|
@@ -6,6 +6,7 @@ import { recordUploadBlocked, recordUploadFailure, recordUploadSuccess, } from "
|
|
|
6
6
|
import { buildLocalAmbientEnvelope, LocalUploadBlockedError, } from "./upload-envelope.js";
|
|
7
7
|
import { applyRawEvidenceUploadOutcomes, hasRetryableEvidenceGap, logEvidenceBackoffBypassed, logEvidenceHeldByBackoff, logPermanentlyRejectedEvidence, partitionHeldEvidenceFiles, permanentEvidenceFailureReason, persistDeliveryAttempts, retrySourcesForFailedSync, retryableEvidenceGapReason, summarizeRawEvidenceDelivery, } from "./upload-evidence-delivery.js";
|
|
8
8
|
import { reportAgentImageArtifacts } from "./upload-agent-artifacts.js";
|
|
9
|
+
import { describeError } from "./health-detail.js";
|
|
9
10
|
import { isNonEmptyString, readResponseJson, responseErrorMessage, } from "./upload-http.js";
|
|
10
11
|
export { LocalUploadBlockedError, buildLocalAmbientEnvelope, } from "./upload-envelope.js";
|
|
11
12
|
export { partitionHeldEvidenceFiles } from "./upload-evidence-delivery.js";
|
|
@@ -76,11 +77,21 @@ export async function syncLocalAmbientEnvelope(options = {}) {
|
|
|
76
77
|
outcomes: uploadOutcomes,
|
|
77
78
|
reused: built.raw_evidence_facts?.reused ?? [],
|
|
78
79
|
cursorObjects: cursor.objects,
|
|
79
|
-
}).catch(() =>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
}).catch((error) => {
|
|
81
|
+
// The whole point of `agent_artifact_report_local_error` is that it is
|
|
82
|
+
// NOT the network error the reporter already labels itself — it is a
|
|
83
|
+
// throw from our own code on the way in. That distinction is only
|
|
84
|
+
// useful if the throw says what it was (BLI-3238).
|
|
85
|
+
console.error("[cockpit-sync] agent image artifact report threw locally", JSON.stringify({
|
|
86
|
+
reason: "agent_artifact_report_local_error",
|
|
87
|
+
...describeError(error),
|
|
88
|
+
}));
|
|
89
|
+
return {
|
|
90
|
+
posted: false,
|
|
91
|
+
reason: "agent_artifact_report_local_error",
|
|
92
|
+
recorded_count: 0,
|
|
93
|
+
};
|
|
94
|
+
});
|
|
84
95
|
await recordIngestedSyncOutcome({
|
|
85
96
|
paths,
|
|
86
97
|
options,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bli-cockpit/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.32",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -26,6 +26,6 @@
|
|
|
26
26
|
"test": "node dist/cli.js --help && node ../../scripts/assert-public-cli-routing.mjs && node ../../scripts/assert-public-package-pack.mjs --workspace=@bli-cockpit/cli"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@bli-cockpit/telemetry-core": "0.1.
|
|
29
|
+
"@bli-cockpit/telemetry-core": "0.1.24"
|
|
30
30
|
}
|
|
31
31
|
}
|