@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.
Files changed (83) hide show
  1. package/dist/adapters/raw-evidence-attribution-gaps.js +133 -0
  2. package/dist/adapters/raw-evidence-claude-reader.js +108 -0
  3. package/dist/adapters/raw-evidence-codex-reader.js +147 -0
  4. package/dist/adapters/raw-evidence-collection-state.js +199 -0
  5. package/dist/adapters/raw-evidence-facts.js +338 -0
  6. package/dist/adapters/raw-evidence-git-diff-reader.js +187 -0
  7. package/dist/adapters/raw-evidence-image-reader.js +107 -0
  8. package/dist/adapters/raw-evidence-sanitize.js +56 -0
  9. package/dist/adapters/raw-evidence-transcript-file.js +182 -0
  10. package/dist/adapters/raw-evidence.js +94 -1203
  11. package/dist/autostart-contract.js +79 -0
  12. package/dist/autostart-darwin-plist.js +265 -0
  13. package/dist/autostart-darwin.js +171 -0
  14. package/dist/autostart-windows-scripts.js +310 -0
  15. package/dist/autostart-windows-task-xml.js +260 -0
  16. package/dist/autostart-windows.js +237 -0
  17. package/dist/autostart-xml.js +23 -0
  18. package/dist/autostart.js +35 -1148
  19. package/dist/commands/agent-rules-command.js +55 -0
  20. package/dist/commands/agent-session-report.js +290 -0
  21. package/dist/commands/analyze.js +131 -0
  22. package/dist/commands/autostart-command.js +105 -0
  23. package/dist/commands/backfill-batches.js +34 -0
  24. package/dist/commands/backfill-candidates.js +54 -0
  25. package/dist/commands/backfill-checkpoint.js +101 -0
  26. package/dist/commands/backfill-command-line.js +70 -0
  27. package/dist/commands/backfill-evidence-outcomes.js +104 -0
  28. package/dist/commands/backfill-issues.js +265 -0
  29. package/dist/commands/backfill-output.js +75 -0
  30. package/dist/commands/backfill-plan.js +71 -0
  31. package/dist/commands/backfill-reasons.js +107 -0
  32. package/dist/commands/backfill-report.js +298 -0
  33. package/dist/commands/backfill-result.js +150 -0
  34. package/dist/commands/backfill-scan.js +274 -0
  35. package/dist/commands/backfill-scope.js +114 -0
  36. package/dist/commands/backfill-session-report.js +145 -0
  37. package/dist/commands/backfill-types.js +1 -0
  38. package/dist/commands/backfill-upload.js +212 -0
  39. package/dist/commands/backfill.js +58 -1705
  40. package/dist/commands/cli-io.js +13 -0
  41. package/dist/commands/doctor.js +57 -0
  42. package/dist/commands/jarvis-trace.js +184 -0
  43. package/dist/commands/jarvis.js +323 -7
  44. package/dist/commands/local-arg-values.js +169 -0
  45. package/dist/commands/local-args-collector.js +604 -0
  46. package/dist/commands/local-args-tower.js +891 -0
  47. package/dist/commands/local-args.js +10 -1549
  48. package/dist/commands/local-help.js +30 -5
  49. package/dist/commands/local.js +21 -1786
  50. package/dist/commands/login.js +53 -0
  51. package/dist/commands/logout.js +66 -0
  52. package/dist/commands/memory-install-claude.js +294 -0
  53. package/dist/commands/memory-install-codex.js +205 -0
  54. package/dist/commands/memory-install-contract.js +231 -0
  55. package/dist/commands/memory-install-files.js +63 -0
  56. package/dist/commands/memory-install-skills.js +121 -0
  57. package/dist/commands/memory-install-toml.js +265 -0
  58. package/dist/commands/memory-install.js +378 -0
  59. package/dist/commands/onboard-receipts.js +66 -0
  60. package/dist/commands/onboard-report.js +274 -0
  61. package/dist/commands/onboard.js +449 -0
  62. package/dist/commands/ops-render.js +36 -0
  63. package/dist/commands/public-root.js +1 -1
  64. package/dist/commands/serve.js +13 -0
  65. package/dist/commands/session-sync.js +513 -534
  66. package/dist/commands/settings-render.js +28 -0
  67. package/dist/commands/settings.js +66 -2
  68. package/dist/commands/start.js +47 -0
  69. package/dist/commands/sync-followups.js +308 -0
  70. package/dist/commands/sync.js +387 -0
  71. package/dist/local-state-attributed-target.js +75 -0
  72. package/dist/local-state-config.js +147 -0
  73. package/dist/local-state-files.js +59 -0
  74. package/dist/local-state-identity.js +73 -0
  75. package/dist/local-state-pairing.js +263 -0
  76. package/dist/local-state-paths.js +61 -0
  77. package/dist/local-state-session.js +68 -0
  78. package/dist/local-state-status.js +163 -0
  79. package/dist/local-state-work-context.js +190 -0
  80. package/dist/local-state.js +34 -848
  81. package/dist/tower-client.js +3 -2
  82. package/dist/tower-stream.js +76 -6
  83. 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
+ }