@bli-cockpit/cli 0.2.237 → 0.2.239
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/attribution-core-fallbacks.js +15 -11
- package/dist/adapters/attribution-core-score.js +111 -87
- package/dist/adapters/car-state.js +38 -35
- package/dist/adapters/claude-attribution-discovery.js +81 -56
- package/dist/adapters/claude-attribution-score.js +108 -94
- package/dist/adapters/claude-attribution-signals.js +52 -42
- package/dist/adapters/codex-attribution.js +42 -33
- package/dist/adapters/codex-session-signals.js +11 -9
- package/dist/adapters/git-state.js +56 -50
- package/dist/adapters/local-sources.js +52 -43
- package/dist/adapters/raw-evidence-git-diff.js +4 -2
- package/dist/adapters/raw-evidence-sanitize.js +15 -12
- package/dist/adapters/raw-evidence-transcript-file.js +32 -31
- package/dist/autostart-self-heal.js +18 -2
- package/dist/commands/agent-session-report.js +51 -59
- package/dist/commands/agent-setup-observed.js +30 -23
- package/dist/commands/agent-setup.js +96 -45
- package/dist/commands/autostart-heal.js +8 -2
- package/dist/commands/autostart-reading.js +3 -0
- package/dist/commands/backfill-evidence-outcomes.js +9 -6
- package/dist/commands/backfill-issues.js +3 -0
- package/dist/commands/backfill-scan.js +27 -22
- package/dist/commands/backfill-upload.js +69 -60
- package/dist/commands/backfill.js +26 -21
- package/dist/commands/brief-done.js +4 -1
- package/dist/commands/brief-edit.js +71 -46
- package/dist/commands/cal-detail.js +35 -0
- package/dist/commands/cal-direct.js +119 -0
- package/dist/commands/cal-overlay.js +73 -30
- package/dist/commands/cal.js +62 -25
- package/dist/commands/clean.js +4 -1
- package/dist/commands/docs-read.js +5 -2
- package/dist/commands/docs.js +38 -6
- package/dist/commands/doctor-backfill.js +88 -71
- package/dist/commands/doctor-pipeline.js +59 -46
- package/dist/commands/heartbeat-post.js +116 -0
- package/dist/commands/heartbeat-token-lock.js +46 -33
- package/dist/commands/heartbeat.js +43 -125
- package/dist/commands/install-update.js +17 -15
- package/dist/commands/issue-show.js +27 -28
- package/dist/commands/issue-versions.js +93 -0
- package/dist/commands/issue-write.js +70 -23
- package/dist/commands/issue.js +73 -37
- package/dist/commands/jarvis-live-answer.js +99 -0
- package/dist/commands/jarvis-render.js +1 -94
- package/dist/commands/jarvis-trace.js +30 -22
- package/dist/commands/jarvis-turn-report.js +124 -0
- package/dist/commands/jarvis-turn.js +108 -182
- package/dist/commands/jarvis.js +26 -17
- package/dist/commands/local-args-collector-setup.js +82 -84
- package/dist/commands/local-args-collector-work.js +37 -38
- package/dist/commands/local-args-tower-admin.js +155 -142
- package/dist/commands/local-args-tower-cal.js +69 -54
- package/dist/commands/local-args-tower-careers.js +73 -54
- package/dist/commands/local-args-tower-docs-msg.js +79 -51
- package/dist/commands/local-args-tower-github.js +26 -20
- package/dist/commands/local-args-tower-hits.js +32 -20
- package/dist/commands/local-args-tower-manager.js +85 -4
- package/dist/commands/local-args-tower-memory.js +139 -117
- package/dist/commands/local-args-tower-msg.js +24 -6
- package/dist/commands/local-args-tower-notes.js +346 -0
- package/dist/commands/local-args-tower-pages.js +100 -378
- package/dist/commands/local-args-tower-settings.js +28 -29
- package/dist/commands/local-args-tower-setup.js +11 -4
- package/dist/commands/local-args-tower-work.js +74 -37
- package/dist/commands/local-args.js +80 -129
- package/dist/commands/local-help-commands-tower-ops.js +15 -4
- package/dist/commands/local-help-commands-tower.js +20 -7
- package/dist/commands/local-help-commands.js +342 -341
- package/dist/commands/local-help.js +87 -83
- package/dist/commands/local-run-guards.js +100 -0
- package/dist/commands/local.js +137 -206
- package/dist/commands/login.js +13 -9
- package/dist/commands/logs.js +55 -38
- package/dist/commands/mail-attachments.js +44 -26
- package/dist/commands/manager-render-home.js +208 -0
- package/dist/commands/manager-render.js +7 -3
- package/dist/commands/manager-requests.js +33 -0
- package/dist/commands/manager.js +39 -5
- package/dist/commands/memory-consolidation.js +18 -9
- package/dist/commands/memory-daemon-probe.js +37 -28
- package/dist/commands/memory-install-config.js +64 -60
- package/dist/commands/memory-install.js +21 -41
- package/dist/commands/memory-requeue.js +26 -19
- package/dist/commands/memory-rules.js +26 -18
- package/dist/commands/memory-search-print.js +26 -0
- package/dist/commands/memory-store.js +14 -30
- package/dist/commands/memory-verb-list.js +49 -48
- package/dist/commands/msg-read-line.js +32 -0
- package/dist/commands/msg.js +45 -3
- package/dist/commands/notes-accounts.js +49 -32
- package/dist/commands/notes-sync-loop.js +115 -89
- package/dist/commands/notes-wisprflow-login.js +44 -23
- package/dist/commands/notes-writes.js +115 -100
- package/dist/commands/notes.js +33 -55
- package/dist/commands/onboard-flows.js +69 -51
- package/dist/commands/onboard.js +11 -9
- package/dist/commands/ops-render-memory.js +10 -7
- package/dist/commands/ops-render.js +33 -30
- package/dist/commands/ops.js +3 -0
- package/dist/commands/public-root.js +1 -1
- package/dist/commands/session-sync-outside-roots.js +40 -36
- package/dist/commands/session-sync-plan.js +14 -10
- package/dist/commands/session-sync-upload.js +24 -22
- package/dist/commands/session-sync.js +19 -8
- package/dist/commands/setup.js +13 -0
- package/dist/commands/slack.js +25 -16
- package/dist/commands/sources-check.js +13 -7
- package/dist/commands/sources-discovery.js +25 -17
- package/dist/commands/sources-verdict.js +19 -18
- package/dist/commands/speed.js +23 -19
- package/dist/commands/start.js +20 -16
- package/dist/commands/sync-doctor.js +52 -48
- package/dist/commands/sync.js +22 -16
- package/dist/commands/usage-telemetry-toml.js +71 -45
- package/dist/commands/usage.js +94 -68
- package/dist/commands/workbook-render.js +10 -7
- package/dist/evidence-reconcile-client.js +75 -64
- package/dist/evidence-redelivery-plan.js +37 -34
- package/dist/evidence-redelivery.js +32 -29
- package/dist/evidence-transcripts-client.js +20 -20
- package/dist/evidence-upload-object.js +86 -89
- package/dist/evidence-upload-plan.js +94 -83
- package/dist/evidence-upload-terminal.js +45 -50
- package/dist/image-description-session.js +156 -159
- package/dist/local-state-pairing-code.js +39 -31
- package/dist/local-state-status.js +9 -2
- package/dist/onboarding-roots.js +5 -8
- package/dist/process-runner.js +8 -8
- package/dist/raw-evidence-gc.js +33 -38
- package/dist/raw-evidence-stuck-summary.js +7 -14
- package/dist/repo-identity.js +26 -23
- package/dist/scheduled-self-update.js +11 -15
- package/dist/second-install.js +3 -0
- package/dist/slim/codex-rules.js +23 -36
- package/dist/spool/local-spool-parse.js +13 -12
- package/dist/sync-lock.js +5 -15
- package/dist/tower-client.js +36 -53
- package/dist/tower-stream.js +7 -24
- package/dist/upload-agent-artifacts.js +58 -64
- package/dist/upload-envelope-build.js +60 -59
- package/dist/upload-evidence-delivery-reconcile.js +58 -56
- package/dist/upload-evidence-delivery-summary.js +32 -34
- package/dist/upload-ingest-receipt.js +20 -23
- package/dist/upload-session-reports-queue.js +4 -1
- package/dist/upload-session-reports-wire.js +64 -68
- package/dist/upload-sync.js +171 -157
- package/package.json +4 -4
package/dist/upload-sync.js
CHANGED
|
@@ -22,157 +22,181 @@ export async function syncLocalAmbientEnvelope(options = {}) {
|
|
|
22
22
|
if (!provenance) {
|
|
23
23
|
throw new Error("Ambient upload requires collector provenance.");
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
const run = {
|
|
26
|
+
options, paths, cursor, built, fetchImpl, provenance, attemptedAt,
|
|
27
|
+
attemptedDate: options.now ?? new Date(attemptedAt),
|
|
28
|
+
staging: await readRawEvidenceStagingState(paths.state_dir),
|
|
29
|
+
uploadOutcomes: [], uploadedChunkCount: 0,
|
|
30
|
+
};
|
|
31
31
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// become held outcomes below, which keeps them out of the envelope, keeps
|
|
35
|
-
// the sync in retry_pending, and names the reason — the 559th identical
|
|
36
|
-
// attempt is what BLI-3066 made impossible. An operator-invoked retry
|
|
37
|
-
// (`cockpit backfill`, doctor) offers them anyway and says so: the window
|
|
38
|
-
// is the scheduler's cadence, not an answer to a person asking now
|
|
39
|
-
// (BLI-3118).
|
|
40
|
-
const { deliverable, held, bypassed } = partitionHeldEvidenceFiles(built.raw_evidence_upload_files, staging, attemptedDate, options.evidenceDeliveryMode);
|
|
41
|
-
logEvidenceHeldByBackoff(held, attemptedAt);
|
|
42
|
-
logEvidenceBackoffBypassed(bypassed, attemptedAt);
|
|
43
|
-
uploadOutcomes = held;
|
|
44
|
-
if (deliverable.length > 0) {
|
|
45
|
-
const upload = await uploadRawEvidenceFilesChunked({
|
|
46
|
-
fetchImpl,
|
|
47
|
-
dashboardUrl: built.dashboard_url,
|
|
48
|
-
deviceToken: built.device_token,
|
|
49
|
-
provenance,
|
|
50
|
-
generatedAt: built.envelope.generated_at,
|
|
51
|
-
files: deliverable,
|
|
52
|
-
});
|
|
53
|
-
uploadOutcomes = [...held, ...upload.outcomes];
|
|
54
|
-
uploadedChunkCount = upload.uploaded_chunk_count;
|
|
55
|
-
}
|
|
56
|
-
await persistDeliveryAttempts(paths.state_dir, staging, uploadOutcomes, attemptedDate);
|
|
57
|
-
}
|
|
58
|
-
const response = await postEnvelopeToIngest({
|
|
59
|
-
fetchImpl,
|
|
60
|
-
built,
|
|
61
|
-
envelope: applyRawEvidenceUploadOutcomes(built.envelope, uploadOutcomes),
|
|
62
|
-
});
|
|
63
|
-
advanceRawEvidenceCursor(cursor, uploadOutcomes, attemptedAt, built.raw_evidence_upload_files);
|
|
64
|
-
await writeRawEvidenceCursor(paths, cursor);
|
|
65
|
-
await reportAgentImageArtifacts({
|
|
66
|
-
fetchImpl,
|
|
67
|
-
dashboardUrl: built.dashboard_url,
|
|
68
|
-
deviceToken: built.device_token,
|
|
69
|
-
provenance,
|
|
70
|
-
generatedAt: built.envelope.generated_at,
|
|
71
|
-
ticketId: built.ticket_id,
|
|
72
|
-
repoFingerprint: built.envelope.work_context.repo_fingerprint,
|
|
73
|
-
worktreeFingerprint: built.envelope.work_context.worktree_fingerprint,
|
|
74
|
-
outcomes: uploadOutcomes,
|
|
75
|
-
reused: built.raw_evidence_facts?.reused ?? [],
|
|
76
|
-
cursorObjects: cursor.objects,
|
|
77
|
-
}).catch((error) => {
|
|
78
|
-
// The whole point of `agent_artifact_report_local_error` is that it is
|
|
79
|
-
// NOT the network error the reporter already labels itself — it is a
|
|
80
|
-
// throw from our own code on the way in. That distinction is only
|
|
81
|
-
// useful if the throw says what it was (BLI-3238).
|
|
82
|
-
console.error("[cockpit-sync] agent image artifact report threw locally", JSON.stringify({
|
|
83
|
-
reason: "agent_artifact_report_local_error",
|
|
84
|
-
...describeError(error),
|
|
85
|
-
}));
|
|
86
|
-
return {
|
|
87
|
-
posted: false,
|
|
88
|
-
reason: "agent_artifact_report_local_error",
|
|
89
|
-
recorded_count: 0,
|
|
90
|
-
};
|
|
91
|
-
});
|
|
92
|
-
await recordIngestedSyncOutcome({
|
|
93
|
-
paths,
|
|
94
|
-
options,
|
|
95
|
-
built,
|
|
96
|
-
uploadOutcomes,
|
|
97
|
-
attemptedAt,
|
|
98
|
-
});
|
|
99
|
-
// The success branch logs too. A line that only fires on failure cannot
|
|
100
|
-
// answer "did anything land at all today?", which is the question that
|
|
101
|
-
// would have caught BLI-2528 in a week instead of 57 days.
|
|
102
|
-
console.error("[cockpit-sync] ingest accepted the envelope", JSON.stringify({
|
|
103
|
-
reason: "ingest_accepted",
|
|
104
|
-
http_status: response.status,
|
|
105
|
-
event_count: built.event_count,
|
|
106
|
-
raw_evidence_file_count: built.raw_evidence_upload_files.length,
|
|
107
|
-
uploaded_chunk_count: uploadedChunkCount,
|
|
108
|
-
failed_object_count: uploadOutcomes.filter((outcome) => outcome.upload_state === "upload_failed").length,
|
|
109
|
-
}));
|
|
110
|
-
return {
|
|
111
|
-
status: "uploaded",
|
|
112
|
-
dashboard_url: built.dashboard_url,
|
|
113
|
-
ticket_id: built.ticket_id,
|
|
114
|
-
work_context_id: built.envelope.work_context.work_context_id,
|
|
115
|
-
head_sha: built.head_sha,
|
|
116
|
-
event_count: built.event_count,
|
|
117
|
-
source_scan_count: built.source_scan_count,
|
|
118
|
-
risk_flag_count: built.risk_flag_count,
|
|
119
|
-
http_status: response.status,
|
|
120
|
-
...summarizeRawEvidenceDelivery(built, uploadOutcomes, uploadedChunkCount, cursor, staging, attemptedDate),
|
|
121
|
-
};
|
|
32
|
+
await deliverSyncEvidence(run);
|
|
33
|
+
return await ingestDeliveredSync(run);
|
|
122
34
|
}
|
|
123
35
|
catch (error) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
36
|
+
return spoolFailedSync(run, error);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function deliverSyncEvidence(run) {
|
|
40
|
+
const { options, paths, built, fetchImpl, provenance, attemptedAt, attemptedDate, staging } = run;
|
|
41
|
+
if (built.raw_evidence_upload_files.length > 0) {
|
|
42
|
+
// Objects still inside their backoff window are not offered at all. They
|
|
43
|
+
// become held outcomes below, which keeps them out of the envelope, keeps
|
|
44
|
+
// the sync in retry_pending, and names the reason — the 559th identical
|
|
45
|
+
// attempt is what BLI-3066 made impossible. An operator-invoked retry
|
|
46
|
+
// (`cockpit backfill`, doctor) offers them anyway and says so: the window
|
|
47
|
+
// is the scheduler's cadence, not an answer to a person asking now
|
|
48
|
+
// (BLI-3118).
|
|
49
|
+
const { deliverable, held, bypassed } = partitionHeldEvidenceFiles(built.raw_evidence_upload_files, staging, attemptedDate, options.evidenceDeliveryMode);
|
|
50
|
+
logEvidenceHeldByBackoff(held, attemptedAt);
|
|
51
|
+
logEvidenceBackoffBypassed(bypassed, attemptedAt);
|
|
52
|
+
run.uploadOutcomes = held;
|
|
53
|
+
if (deliverable.length > 0) {
|
|
54
|
+
const upload = await uploadRawEvidenceFilesChunked({
|
|
55
|
+
fetchImpl,
|
|
56
|
+
dashboardUrl: built.dashboard_url,
|
|
57
|
+
deviceToken: built.device_token,
|
|
58
|
+
provenance,
|
|
59
|
+
generatedAt: built.envelope.generated_at,
|
|
60
|
+
files: deliverable,
|
|
61
|
+
});
|
|
62
|
+
run.uploadOutcomes = [...held, ...upload.outcomes];
|
|
63
|
+
run.uploadedChunkCount = upload.uploaded_chunk_count;
|
|
64
|
+
}
|
|
65
|
+
await persistDeliveryAttempts(paths.state_dir, staging, run.uploadOutcomes, attemptedDate);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function ingestDeliveredSync(run) {
|
|
69
|
+
const { paths, options, built, fetchImpl, cursor, attemptedAt, attemptedDate, staging, uploadOutcomes, uploadedChunkCount } = run;
|
|
70
|
+
const response = await postEnvelopeToIngest({
|
|
71
|
+
fetchImpl,
|
|
72
|
+
built,
|
|
73
|
+
envelope: applyRawEvidenceUploadOutcomes(built.envelope, uploadOutcomes),
|
|
74
|
+
});
|
|
75
|
+
advanceRawEvidenceCursor(cursor, uploadOutcomes, attemptedAt, built.raw_evidence_upload_files);
|
|
76
|
+
await writeRawEvidenceCursor(paths, cursor);
|
|
77
|
+
await reportSyncArtifacts(run);
|
|
78
|
+
await recordIngestedSyncOutcome({
|
|
79
|
+
paths,
|
|
80
|
+
options,
|
|
81
|
+
built,
|
|
82
|
+
uploadOutcomes,
|
|
83
|
+
attemptedAt,
|
|
84
|
+
});
|
|
85
|
+
logAcceptedSync(run, response);
|
|
86
|
+
return {
|
|
87
|
+
status: "uploaded",
|
|
88
|
+
dashboard_url: built.dashboard_url,
|
|
89
|
+
ticket_id: built.ticket_id,
|
|
90
|
+
work_context_id: built.envelope.work_context.work_context_id,
|
|
91
|
+
head_sha: built.head_sha,
|
|
92
|
+
event_count: built.event_count,
|
|
93
|
+
source_scan_count: built.source_scan_count,
|
|
94
|
+
risk_flag_count: built.risk_flag_count,
|
|
95
|
+
http_status: response.status,
|
|
96
|
+
...summarizeRawEvidenceDelivery(built, uploadOutcomes, uploadedChunkCount, cursor, staging, attemptedDate),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
async function reportSyncArtifacts(run) {
|
|
100
|
+
const { built, fetchImpl, provenance, cursor, uploadOutcomes } = run;
|
|
101
|
+
await reportAgentImageArtifacts({
|
|
102
|
+
fetchImpl,
|
|
103
|
+
dashboardUrl: built.dashboard_url,
|
|
104
|
+
deviceToken: built.device_token,
|
|
105
|
+
provenance,
|
|
106
|
+
generatedAt: built.envelope.generated_at,
|
|
107
|
+
ticketId: built.ticket_id,
|
|
108
|
+
repoFingerprint: built.envelope.work_context.repo_fingerprint,
|
|
109
|
+
worktreeFingerprint: built.envelope.work_context.worktree_fingerprint,
|
|
110
|
+
outcomes: uploadOutcomes,
|
|
111
|
+
reused: built.raw_evidence_facts?.reused ?? [],
|
|
112
|
+
cursorObjects: cursor.objects,
|
|
113
|
+
}).catch((error) => {
|
|
114
|
+
// The whole point of `agent_artifact_report_local_error` is that it is
|
|
115
|
+
// NOT the network error the reporter already labels itself — it is a
|
|
116
|
+
// throw from our own code on the way in. That distinction is only
|
|
117
|
+
// useful if the throw says what it was (BLI-3238).
|
|
118
|
+
console.error("[cockpit-sync] agent image artifact report threw locally", JSON.stringify({
|
|
119
|
+
reason: "agent_artifact_report_local_error",
|
|
120
|
+
...describeError(error),
|
|
143
121
|
}));
|
|
144
|
-
const entry = await recordUploadFailure(paths, {
|
|
145
|
-
last_attempt_at: attemptedAt,
|
|
146
|
-
dashboard_url: built.dashboard_url,
|
|
147
|
-
work_context_id: built.envelope.work_context.work_context_id,
|
|
148
|
-
ticket_id: built.ticket_id,
|
|
149
|
-
repo_label: built.repo_label,
|
|
150
|
-
branch: built.envelope.work_context.branch,
|
|
151
|
-
event_count: built.event_count,
|
|
152
|
-
source_scan_count: built.source_scan_count,
|
|
153
|
-
risk_flag_count: built.risk_flag_count,
|
|
154
|
-
raw_evidence_file_count: built.raw_evidence_upload_files.length,
|
|
155
|
-
retry_sources: retrySourcesForFailedSync(options, built.raw_evidence_facts),
|
|
156
|
-
failure_reason: spooledFailureReason,
|
|
157
|
-
retry_command: "cockpit sync",
|
|
158
|
-
});
|
|
159
122
|
return {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
work_context_id: built.envelope.work_context.work_context_id,
|
|
164
|
-
head_sha: built.head_sha,
|
|
165
|
-
event_count: built.event_count,
|
|
166
|
-
source_scan_count: built.source_scan_count,
|
|
167
|
-
risk_flag_count: built.risk_flag_count,
|
|
168
|
-
failure_reason: spooledFailureReason,
|
|
169
|
-
failure_class: classified.reason,
|
|
170
|
-
failure_http_status: classified.http_status,
|
|
171
|
-
spool_entry_id: entry.spool_id,
|
|
172
|
-
retry_command: entry.retry_command,
|
|
173
|
-
...summarizeRawEvidenceDelivery(built, uploadOutcomes, uploadedChunkCount, cursor, staging, attemptedDate),
|
|
123
|
+
posted: false,
|
|
124
|
+
reason: "agent_artifact_report_local_error",
|
|
125
|
+
recorded_count: 0,
|
|
174
126
|
};
|
|
175
|
-
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function logAcceptedSync(run, response) {
|
|
130
|
+
const { built, uploadedChunkCount, uploadOutcomes } = run;
|
|
131
|
+
// The success branch logs too. A line that only fires on failure cannot
|
|
132
|
+
// answer "did anything land at all today?", which is the question that
|
|
133
|
+
// would have caught BLI-2528 in a week instead of 57 days.
|
|
134
|
+
console.error("[cockpit-sync] ingest accepted the envelope", JSON.stringify({
|
|
135
|
+
reason: "ingest_accepted",
|
|
136
|
+
http_status: response.status,
|
|
137
|
+
event_count: built.event_count,
|
|
138
|
+
raw_evidence_file_count: built.raw_evidence_upload_files.length,
|
|
139
|
+
uploaded_chunk_count: uploadedChunkCount,
|
|
140
|
+
failed_object_count: uploadOutcomes.filter((outcome) => outcome.upload_state === "upload_failed").length,
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
async function spoolFailedSync(run, error) {
|
|
144
|
+
const { paths, options, built, attemptedAt, attemptedDate, cursor, staging, uploadOutcomes, uploadedChunkCount } = run;
|
|
145
|
+
// The spool row used to store `error.message` verbatim, so every distinct
|
|
146
|
+
// server sentence became its own "reason" and nothing could count how many
|
|
147
|
+
// machines were failing the same way (BLI-3483). It now stores a label from
|
|
148
|
+
// a closed set plus a bounded, redacted cause.
|
|
149
|
+
const classified = classifySyncFailure(error);
|
|
150
|
+
// A committed content-addressed object may be shared by concurrent syncs.
|
|
151
|
+
// Never delete it from this error path: an ingest retry can reuse it, while
|
|
152
|
+
// client-side cleanup cannot prove exclusive ownership without racing a
|
|
153
|
+
// second sync that is about to index the same object.
|
|
154
|
+
const spooledFailureReason = classified.failure_reason;
|
|
155
|
+
logSpooledSyncFailure(run, classified);
|
|
156
|
+
const entry = await recordUploadFailure(paths, {
|
|
157
|
+
last_attempt_at: attemptedAt,
|
|
158
|
+
dashboard_url: built.dashboard_url,
|
|
159
|
+
work_context_id: built.envelope.work_context.work_context_id,
|
|
160
|
+
ticket_id: built.ticket_id,
|
|
161
|
+
repo_label: built.repo_label,
|
|
162
|
+
branch: built.envelope.work_context.branch,
|
|
163
|
+
event_count: built.event_count,
|
|
164
|
+
source_scan_count: built.source_scan_count,
|
|
165
|
+
risk_flag_count: built.risk_flag_count,
|
|
166
|
+
raw_evidence_file_count: built.raw_evidence_upload_files.length,
|
|
167
|
+
retry_sources: retrySourcesForFailedSync(options, built.raw_evidence_facts),
|
|
168
|
+
failure_reason: spooledFailureReason,
|
|
169
|
+
retry_command: "cockpit sync",
|
|
170
|
+
});
|
|
171
|
+
return {
|
|
172
|
+
status: "spooled",
|
|
173
|
+
dashboard_url: built.dashboard_url,
|
|
174
|
+
ticket_id: built.ticket_id,
|
|
175
|
+
work_context_id: built.envelope.work_context.work_context_id,
|
|
176
|
+
head_sha: built.head_sha,
|
|
177
|
+
event_count: built.event_count,
|
|
178
|
+
source_scan_count: built.source_scan_count,
|
|
179
|
+
risk_flag_count: built.risk_flag_count,
|
|
180
|
+
failure_reason: spooledFailureReason,
|
|
181
|
+
failure_class: classified.reason,
|
|
182
|
+
failure_http_status: classified.http_status,
|
|
183
|
+
spool_entry_id: entry.spool_id,
|
|
184
|
+
retry_command: entry.retry_command,
|
|
185
|
+
...summarizeRawEvidenceDelivery(built, uploadOutcomes, uploadedChunkCount, cursor, staging, attemptedDate),
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function logSpooledSyncFailure(run, classified) {
|
|
189
|
+
const { built, uploadedChunkCount } = run;
|
|
190
|
+
console.error("[cockpit-sync] sync spooled for retry", JSON.stringify({
|
|
191
|
+
reason: classified.reason,
|
|
192
|
+
...(classified.http_status === null
|
|
193
|
+
? {}
|
|
194
|
+
: { http_status: classified.http_status }),
|
|
195
|
+
...(classified.detail ? { detail: classified.detail } : {}),
|
|
196
|
+
event_count: built.event_count,
|
|
197
|
+
raw_evidence_file_count: built.raw_evidence_upload_files.length,
|
|
198
|
+
uploaded_chunk_count: uploadedChunkCount,
|
|
199
|
+
}));
|
|
176
200
|
}
|
|
177
201
|
/**
|
|
178
202
|
* Assemble the envelope, and if this machine simply cannot upload, say so in
|
|
@@ -225,18 +249,8 @@ function advanceRawEvidenceCursor(cursor, outcomes, attemptedAt, files = []) {
|
|
|
225
249
|
cursor.updated_at = attemptedAt;
|
|
226
250
|
}
|
|
227
251
|
/**
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
* | evidence state | ledger |
|
|
232
|
-
* |---------------------------------------|-------------------------------------|
|
|
233
|
-
* | a gap a later sync could rescue | success, then a queued retry naming why |
|
|
234
|
-
* | a gap no retry can rescue | success, then a blocker naming why, plus a stderr record |
|
|
235
|
-
* | nothing missing | success, pending state cleared |
|
|
236
|
-
*
|
|
237
|
-
* The middle row is the one that matters. A permanently rejected object gets no
|
|
238
|
-
* retry — nothing would change — but it must still be named, or `cockpit status`
|
|
239
|
-
* reads clean on a machine with missing collection (BLI-2528).
|
|
252
|
+
* Record success before queuing a rescuable gap or blocking a permanent one.
|
|
253
|
+
* A permanently rejected object gets no retry but remains visible in status.
|
|
240
254
|
*/
|
|
241
255
|
async function recordIngestedSyncOutcome(options) {
|
|
242
256
|
const { paths, built, uploadOutcomes, attemptedAt } = 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.239",
|
|
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.70",
|
|
31
|
+
"@bli-cockpit/mcp": "0.1.144",
|
|
32
|
+
"@bli-cockpit/telemetry-core": "0.1.77"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|