@bli-cockpit/cli 0.2.48 → 0.2.50
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/raw-evidence-attribution-gaps.js +133 -0
- package/dist/adapters/raw-evidence-claude-reader.js +108 -0
- package/dist/adapters/raw-evidence-codex-reader.js +147 -0
- package/dist/adapters/raw-evidence-collection-state.js +199 -0
- package/dist/adapters/raw-evidence-facts.js +338 -0
- package/dist/adapters/raw-evidence-git-diff-reader.js +187 -0
- package/dist/adapters/raw-evidence-image-reader.js +107 -0
- package/dist/adapters/raw-evidence-sanitize.js +56 -0
- package/dist/adapters/raw-evidence-transcript-file.js +182 -0
- package/dist/adapters/raw-evidence.js +94 -1203
- package/dist/autostart-contract.js +79 -0
- package/dist/autostart-darwin-plist.js +265 -0
- package/dist/autostart-darwin.js +171 -0
- package/dist/autostart-windows-scripts.js +310 -0
- package/dist/autostart-windows-task-xml.js +260 -0
- package/dist/autostart-windows.js +237 -0
- package/dist/autostart-xml.js +23 -0
- package/dist/autostart.js +35 -1148
- package/dist/commands/agent-rules-command.js +55 -0
- package/dist/commands/agent-session-report.js +290 -0
- package/dist/commands/analyze.js +131 -0
- package/dist/commands/autostart-command.js +105 -0
- package/dist/commands/backfill-batches.js +34 -0
- package/dist/commands/backfill-candidates.js +54 -0
- package/dist/commands/backfill-checkpoint.js +101 -0
- package/dist/commands/backfill-command-line.js +70 -0
- package/dist/commands/backfill-evidence-outcomes.js +104 -0
- package/dist/commands/backfill-issues.js +265 -0
- package/dist/commands/backfill-output.js +75 -0
- package/dist/commands/backfill-plan.js +71 -0
- package/dist/commands/backfill-reasons.js +107 -0
- package/dist/commands/backfill-report.js +298 -0
- package/dist/commands/backfill-result.js +150 -0
- package/dist/commands/backfill-scan.js +274 -0
- package/dist/commands/backfill-scope.js +114 -0
- package/dist/commands/backfill-session-report.js +145 -0
- package/dist/commands/backfill-types.js +1 -0
- package/dist/commands/backfill-upload.js +212 -0
- package/dist/commands/backfill.js +58 -1705
- package/dist/commands/cli-io.js +13 -0
- package/dist/commands/doctor.js +57 -0
- package/dist/commands/jarvis-trace.js +184 -0
- package/dist/commands/jarvis.js +323 -7
- package/dist/commands/local-arg-values.js +169 -0
- package/dist/commands/local-args-collector.js +604 -0
- package/dist/commands/local-args-tower.js +891 -0
- package/dist/commands/local-args.js +10 -1549
- package/dist/commands/local-help.js +30 -5
- package/dist/commands/local.js +21 -1786
- package/dist/commands/login.js +53 -0
- package/dist/commands/logout.js +66 -0
- package/dist/commands/memory-install-claude.js +294 -0
- package/dist/commands/memory-install-codex.js +205 -0
- package/dist/commands/memory-install-contract.js +231 -0
- package/dist/commands/memory-install-files.js +63 -0
- package/dist/commands/memory-install-skills.js +121 -0
- package/dist/commands/memory-install-toml.js +265 -0
- package/dist/commands/memory-install.js +378 -0
- package/dist/commands/onboard-receipts.js +66 -0
- package/dist/commands/onboard-report.js +274 -0
- package/dist/commands/onboard.js +449 -0
- package/dist/commands/ops-render.js +36 -0
- package/dist/commands/public-root.js +1 -1
- package/dist/commands/serve.js +13 -0
- package/dist/commands/session-sync.js +513 -534
- package/dist/commands/settings-render.js +28 -0
- package/dist/commands/settings.js +66 -2
- package/dist/commands/start.js +47 -0
- package/dist/commands/sync-followups.js +308 -0
- package/dist/commands/sync.js +387 -0
- package/dist/local-state-attributed-target.js +75 -0
- package/dist/local-state-config.js +147 -0
- package/dist/local-state-files.js +59 -0
- package/dist/local-state-identity.js +73 -0
- package/dist/local-state-pairing.js +263 -0
- package/dist/local-state-paths.js +61 -0
- package/dist/local-state-session.js +68 -0
- package/dist/local-state-status.js +163 -0
- package/dist/local-state-work-context.js +190 -0
- package/dist/local-state.js +34 -848
- package/dist/tower-client.js +3 -2
- package/dist/tower-stream.js +76 -6
- package/package.json +2 -1
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One transcript, end to end: read it, mask it, name it, pay for it, stage it.
|
|
3
|
+
*
|
|
4
|
+
* The Codex reader, the Claude reader and the sidecar path all funnel through
|
|
5
|
+
* `collectOneEvidenceFile`, so the order of the guards is the contract: delivery
|
|
6
|
+
* backoff BEFORE the read (a held object costs nothing this cycle), the size cap
|
|
7
|
+
* before the buffer, masking before the content hash, and the reuse check before
|
|
8
|
+
* the budget — a file already durable remotely must not spend a budget it does
|
|
9
|
+
* not need.
|
|
10
|
+
*
|
|
11
|
+
* Every refusal pushes a named skip into the gap ledger first, so a file this
|
|
12
|
+
* module declines is never silently absent.
|
|
13
|
+
*/
|
|
14
|
+
import fs from "node:fs/promises";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { describeError } from "../health-detail.js";
|
|
17
|
+
import { DELIVERY_BACKOFF_HOLDING_REASON, evidenceSourceKey, } from "../raw-evidence-staging.js";
|
|
18
|
+
import { isSecretLikePath, sha256, shortHash, } from "./raw-evidence-keys.js";
|
|
19
|
+
import { maskSecretsInTranscript, } from "./raw-evidence-sanitize.js";
|
|
20
|
+
import { markBudgetCapApplied, markCapApplied, } from "./raw-evidence-completeness.js";
|
|
21
|
+
import { evidenceEntry } from "./raw-evidence-manifest.js";
|
|
22
|
+
import { admitToBudget, stageEvidenceBytes, } from "./raw-evidence-collection-state.js";
|
|
23
|
+
/**
|
|
24
|
+
* Reads, secret-guards, content-addresses, budget-checks, and copies one
|
|
25
|
+
* attributed transcript into the pack.
|
|
26
|
+
*
|
|
27
|
+
* Returns whether this file's *content* is accounted for, which is not the same
|
|
28
|
+
* as "was staged": a transcript whose bytes are already durable remotely
|
|
29
|
+
* returns true without staging anything, and its images are still worth
|
|
30
|
+
* collecting. Every false return has pushed a named skip first.
|
|
31
|
+
*/
|
|
32
|
+
export async function collectOneEvidenceFile(collection, options) {
|
|
33
|
+
const names = nameEvidenceFile(options);
|
|
34
|
+
const skip = (reason) => {
|
|
35
|
+
collection.skipped.push({
|
|
36
|
+
kind: options.kind,
|
|
37
|
+
label: names.evidenceLabel,
|
|
38
|
+
reason,
|
|
39
|
+
});
|
|
40
|
+
return false;
|
|
41
|
+
};
|
|
42
|
+
// Delivery backoff is checked BEFORE the file is read. An object whose commit
|
|
43
|
+
// has failed repeatedly costs nothing at all this cycle — no read, no hash,
|
|
44
|
+
// no copy, no request — and the hold is a named, retryable gap so a held
|
|
45
|
+
// session cannot make the sync look clean (BLI-3066).
|
|
46
|
+
if (collection.heldSources.has(names.sourceKey)) {
|
|
47
|
+
collection.deliveryHeldCount += 1;
|
|
48
|
+
skip(DELIVERY_BACKOFF_HOLDING_REASON);
|
|
49
|
+
logDeliveryBackoffHold(options.kind, names.sourceKey);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const read = await readEvidenceFileWithinCap(options.filePath, options.maxFileBytes);
|
|
53
|
+
if (read.status === "read_failed")
|
|
54
|
+
return skip("file_read_failed");
|
|
55
|
+
if (read.status === "too_large") {
|
|
56
|
+
markCapApplied(collection, options.kind, "max_file_bytes");
|
|
57
|
+
return skip("file_too_large");
|
|
58
|
+
}
|
|
59
|
+
const sanitized = maskSecretsInTranscript(collection, {
|
|
60
|
+
raw: read.bytes,
|
|
61
|
+
kind: options.kind,
|
|
62
|
+
evidenceLabel: names.evidenceLabel,
|
|
63
|
+
secretLikeFileName: names.secretLikeFileName,
|
|
64
|
+
});
|
|
65
|
+
const contentHash = sha256(sanitized.bytes);
|
|
66
|
+
if (collection.skipContentHashes.has(contentHash)) {
|
|
67
|
+
collection.reused.push({
|
|
68
|
+
kind: options.kind,
|
|
69
|
+
label: names.evidenceLabel,
|
|
70
|
+
content_hash_sha256: contentHash,
|
|
71
|
+
codex_session_id: options.sessionId,
|
|
72
|
+
});
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
const deferReason = admitToBudget(collection.budget, sanitized.bytes.byteLength);
|
|
76
|
+
if (deferReason) {
|
|
77
|
+
markBudgetCapApplied(collection, deferReason);
|
|
78
|
+
return skip(deferReason);
|
|
79
|
+
}
|
|
80
|
+
await stageOneTranscript(collection, {
|
|
81
|
+
...options,
|
|
82
|
+
names,
|
|
83
|
+
sanitized,
|
|
84
|
+
contentHash,
|
|
85
|
+
});
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
function nameEvidenceFile(options) {
|
|
89
|
+
const fileName = path.basename(options.filePath);
|
|
90
|
+
const secretLikeFileName = isSecretLikePath(fileName);
|
|
91
|
+
return {
|
|
92
|
+
secretLikeFileName,
|
|
93
|
+
evidenceLabel: secretLikeFileName ? "[REDACTED_FILE_NAME]" : fileName,
|
|
94
|
+
packedFileName: secretLikeFileName ? "redacted-file.jsonl" : fileName,
|
|
95
|
+
sourceKey: evidenceSourceKey({
|
|
96
|
+
kind: options.kind,
|
|
97
|
+
sessionId: options.sessionId,
|
|
98
|
+
sourcePath: options.filePath,
|
|
99
|
+
}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function logDeliveryBackoffHold(kind, sourceKey) {
|
|
103
|
+
console.error("[raw-evidence] delivery backoff holding source", JSON.stringify({
|
|
104
|
+
reason: DELIVERY_BACKOFF_HOLDING_REASON,
|
|
105
|
+
kind,
|
|
106
|
+
source_key: sourceKey,
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
/** Put an accepted transcript's bytes on disk and add its manifest entry. */
|
|
110
|
+
async function stageOneTranscript(collection, file) {
|
|
111
|
+
collection.index.value += 1;
|
|
112
|
+
const relativePath = path.join("files", `${String(collection.index.value).padStart(3, "0")}-${shortHash(file.filePath)}-${file.names.packedFileName}`);
|
|
113
|
+
const staged = await stageEvidenceBytes(collection, {
|
|
114
|
+
contentHash: file.contentHash,
|
|
115
|
+
bytes: file.sanitized.bytes,
|
|
116
|
+
fileName: path.basename(relativePath),
|
|
117
|
+
kind: file.kind,
|
|
118
|
+
sourceKey: file.names.sourceKey,
|
|
119
|
+
});
|
|
120
|
+
collection.entries.push(evidenceEntry({
|
|
121
|
+
context: collection.context,
|
|
122
|
+
kind: file.kind,
|
|
123
|
+
packId: collection.packId,
|
|
124
|
+
localPath: staged.local_path,
|
|
125
|
+
relativePath,
|
|
126
|
+
mediaType: file.mediaType,
|
|
127
|
+
redactedSummary: file.sanitized.status === "redacted"
|
|
128
|
+
? `${file.redactedSummary} Secret-like values were deterministically redacted before upload.`
|
|
129
|
+
: file.redactedSummary,
|
|
130
|
+
// Both branches carry a record now (BLI-3277), so "was anything
|
|
131
|
+
// replaced?" is the status, never the presence of `redaction`.
|
|
132
|
+
redaction: file.sanitized.redaction,
|
|
133
|
+
bytes: file.sanitized.bytes,
|
|
134
|
+
codexSessionId: file.sessionId,
|
|
135
|
+
contentAddress: file.contentAddress(file.contentHash.slice(0, 16)),
|
|
136
|
+
stagedInPack: staged.staged_in_pack,
|
|
137
|
+
sourceKey: file.names.sourceKey,
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Size is checked twice on purpose: once by `stat` so an oversized transcript
|
|
142
|
+
* is never buffered at all, and once on the bytes actually read, because a live
|
|
143
|
+
* session can grow between the two.
|
|
144
|
+
*/
|
|
145
|
+
async function readEvidenceFileWithinCap(filePath, maxFileBytes) {
|
|
146
|
+
if (maxFileBytes) {
|
|
147
|
+
let stat;
|
|
148
|
+
try {
|
|
149
|
+
stat = await fs.stat(filePath);
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
// The caller turns this into the `file_read_failed` skip label, which is
|
|
153
|
+
// the one label an operator can do nothing with. The file was discovered
|
|
154
|
+
// moments ago, so a failure here is a rotated session, a permission
|
|
155
|
+
// problem or a dead symlink — three different answers (BLI-3238).
|
|
156
|
+
console.error("[raw-evidence] evidence file could not be stat'd", JSON.stringify({
|
|
157
|
+
reason: "file_read_failed",
|
|
158
|
+
stage: "stat",
|
|
159
|
+
...describeError(error),
|
|
160
|
+
}));
|
|
161
|
+
return { status: "read_failed" };
|
|
162
|
+
}
|
|
163
|
+
if (stat.size > maxFileBytes)
|
|
164
|
+
return { status: "too_large" };
|
|
165
|
+
}
|
|
166
|
+
let bytes;
|
|
167
|
+
try {
|
|
168
|
+
bytes = await fs.readFile(filePath);
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
console.error("[raw-evidence] evidence file could not be read", JSON.stringify({
|
|
172
|
+
reason: "file_read_failed",
|
|
173
|
+
stage: "read",
|
|
174
|
+
...describeError(error),
|
|
175
|
+
}));
|
|
176
|
+
return { status: "read_failed" };
|
|
177
|
+
}
|
|
178
|
+
if (maxFileBytes && bytes.byteLength > maxFileBytes) {
|
|
179
|
+
return { status: "too_large" };
|
|
180
|
+
}
|
|
181
|
+
return { status: "ok", bytes };
|
|
182
|
+
}
|