@bli-cockpit/cli 0.2.29 → 0.2.31
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/README.md +22 -15
- 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 +46 -2
- 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/collection-report.js +25 -21
- package/dist/commands/doctor.js +54 -21
- package/dist/commands/install-receipts.js +43 -6
- package/dist/commands/install-update.js +3 -1
- package/dist/commands/local-args.js +4 -0
- package/dist/commands/local-auth.js +14 -0
- package/dist/commands/local-help.js +9 -9
- package/dist/commands/local.js +34 -15
- package/dist/commands/public-root.js +1 -1
- package/dist/commands/session-sync.js +35 -6
- package/dist/commands/status.js +82 -27
- 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 +6 -1
- 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
|
@@ -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.31",
|
|
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.23"
|
|
30
30
|
}
|
|
31
31
|
}
|