@bli-cockpit/cli 0.2.245 → 0.2.246
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/commands/delivery.js +37 -0
- package/dist/commands/local-args-tower.js +2 -1
- package/dist/commands/local-args.js +2 -1
- package/dist/commands/local-help-commands-tower-ops.js +8 -0
- package/dist/commands/local-help.js +2 -0
- package/dist/commands/local-run-ops.js +2 -0
- package/dist/commands/local.js +1 -0
- package/dist/commands/public-root.js +1 -1
- package/dist/staging-gate.js +24 -3
- package/package.json +2 -2
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { askAgentDoor, emitAgentDoor, failAgentDoor, openAgentDoor } from "./agent-door.js";
|
|
2
|
+
import { writeLine } from "./cli-io.js";
|
|
3
|
+
import { parseNamedArgs, optionalNonEmpty, optionalUrl } from "./local-arg-values.js";
|
|
4
|
+
const USAGE = "Usage: cockpit delivery report [--days <1-30>] [--end <YYYY-MM-DD>] [--review-minutes <1-240>] [--json]";
|
|
5
|
+
function bounded(raw, min, max) {
|
|
6
|
+
if (raw === undefined)
|
|
7
|
+
return undefined;
|
|
8
|
+
const n = Number(raw);
|
|
9
|
+
if (!Number.isInteger(n) || n < min || n > max)
|
|
10
|
+
throw new Error(USAGE);
|
|
11
|
+
return n;
|
|
12
|
+
}
|
|
13
|
+
export function parseDeliveryArgs(args) {
|
|
14
|
+
const values = parseNamedArgs(args, { allowedFlags: ["--days", "--end", "--review-minutes", "--json", "--home", "--dashboard-url"], valueFlags: ["--days", "--end", "--review-minutes", "--home", "--dashboard-url"] });
|
|
15
|
+
const end = values.flags.get("--end");
|
|
16
|
+
if (values.positionals.length !== 1 || values.positionals[0] !== "report" || (end !== undefined && !/^\d{4}-\d{2}-\d{2}$/.test(end)))
|
|
17
|
+
throw new Error(USAGE);
|
|
18
|
+
return { kind: "delivery", action: "report", days: bounded(values.flags.get("--days"), 1, 30), end, reviewMinutes: bounded(values.flags.get("--review-minutes"), 1, 240), json: values.booleans.has("--json"), homeDir: optionalNonEmpty(values.flags.get("--home")), dashboardUrl: optionalUrl(values.flags.get("--dashboard-url")) };
|
|
19
|
+
}
|
|
20
|
+
export async function runDelivery(command, io) {
|
|
21
|
+
const door = await openAgentDoor("delivery", command, io);
|
|
22
|
+
const query = new URLSearchParams();
|
|
23
|
+
if (command.days !== undefined)
|
|
24
|
+
query.set("days", String(command.days));
|
|
25
|
+
if (command.end !== undefined)
|
|
26
|
+
query.set("end", command.end);
|
|
27
|
+
if (command.reviewMinutes !== undefined)
|
|
28
|
+
query.set("review_minutes", String(command.reviewMinutes));
|
|
29
|
+
const answer = await askAgentDoor(door, { path: `/api/delivery${query.size ? `?${query}` : ""}`, method: "GET", label: "delivery report", timeoutMs: 30_000 });
|
|
30
|
+
if (!answer.ok)
|
|
31
|
+
return failAgentDoor(door, "[delivery cli]", answer.reason, answer.detail);
|
|
32
|
+
if (command.json)
|
|
33
|
+
return emitAgentDoor(door, answer.body);
|
|
34
|
+
for (const line of answer.body.lines)
|
|
35
|
+
writeLine(io.stdout, line);
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
@@ -56,4 +56,5 @@ export { parseRenameArgs } from './local-args-tower-rename.js';
|
|
|
56
56
|
export { parseGlossaryArgs } from "./glossary.js";
|
|
57
57
|
export { parseSpendArgs } from "./spend.js";
|
|
58
58
|
export { parsePushArgs } from "./push.js";
|
|
59
|
-
export { parseCiArgs } from "./ci.js";
|
|
59
|
+
export { parseCiArgs } from "./ci.js";
|
|
60
|
+
export { parseDeliveryArgs } from "./delivery.js";
|
|
@@ -22,7 +22,7 @@ import { parseRootsArgs } from "./roots.js";
|
|
|
22
22
|
import { parseMilestoneArgs } from "./local-args-tower-milestone.js";
|
|
23
23
|
import { SURFACE_ALIASES } from "./local-help.js";
|
|
24
24
|
import { parseAgentRulesArgs, parseAnalyzeArgs, parseAutostartArgs, parseBackfillArgs, parseCleanArgs, parseDoctorArgs, parseInstallArgs, parseLoginArgs, parseMemoryArgs, parseLogoutArgs, parseOnboardArgs, parseReleaseArgs, parseServeArgs, parseSessionsArgs, parseSourcesArgs, parseStartArgs, parseStatusArgs, parseSyncArgs, parseUpdateArgs, } from "./local-args-collector.js";
|
|
25
|
-
import { parseCiArgs, parseBriefArgs, parseCorrectArgs, parseDocsArgs, parseIssueArgs, parseJarvisArgs, parseMailArgs, parseCalArgs, parseModelArgs, parseMsgArgs, parseNotesArgs, parseOpsArgs, parseProjectArgs, parseModelsArgs, parseScoutArgs, parseGithubArgs, parseSearchArgs, parseSettingsArgs, parseSlackArgs, parseTeamArgs, parseWorkbookArgs, parseUsageArgs, parseSetupArgs, parseSpeedArgs, parseLogsArgs, parseBrainArgs, parseHitsArgs, parseIntentionsArgs, parseWhoArgs, parsePeopleArgs, parseApprovalsArgs, parseDrillsArgs, parseManagerArgs, parseImageArgs, parseCareersArgs, parseRenameArgs, } from "./local-args-tower.js";
|
|
25
|
+
import { parseCiArgs, parseDeliveryArgs, parseBriefArgs, parseCorrectArgs, parseDocsArgs, parseIssueArgs, parseJarvisArgs, parseMailArgs, parseCalArgs, parseModelArgs, parseMsgArgs, parseNotesArgs, parseOpsArgs, parseProjectArgs, parseModelsArgs, parseScoutArgs, parseGithubArgs, parseSearchArgs, parseSettingsArgs, parseSlackArgs, parseTeamArgs, parseWorkbookArgs, parseUsageArgs, parseSetupArgs, parseSpeedArgs, parseLogsArgs, parseBrainArgs, parseHitsArgs, parseIntentionsArgs, parseWhoArgs, parsePeopleArgs, parseApprovalsArgs, parseDrillsArgs, parseManagerArgs, parseImageArgs, parseCareersArgs, parseRenameArgs, } from "./local-args-tower.js";
|
|
26
26
|
// `normalizeUrl` has always been part of this module's surface — `local.ts` and
|
|
27
27
|
// `local-auth.ts` import it from here — so it stays exported from this address
|
|
28
28
|
// even though it now lives next door. The same goes for the four names the
|
|
@@ -65,6 +65,7 @@ const COMMAND_PARSERS = new Map([
|
|
|
65
65
|
["models", (_command, rest) => parseModelsArgs(rest)],
|
|
66
66
|
["scout", (_command, rest) => parseScoutArgs(rest)],
|
|
67
67
|
["ci", (_command, rest) => parseCiArgs(rest)],
|
|
68
|
+
["delivery", (_command, rest) => parseDeliveryArgs(rest)],
|
|
68
69
|
["ops", (_command, rest) => parseOpsArgs(rest)],
|
|
69
70
|
["slack", (_command, rest) => parseSlackArgs(rest)],
|
|
70
71
|
["settings", (_command, rest) => parseSettingsArgs(rest)],
|
|
@@ -395,6 +395,14 @@ export const TOWER_OPS_COMMAND_HELP = [
|
|
|
395
395
|
"ci",
|
|
396
396
|
["Usage: cockpit ci report [--repo <owner/name>] [--json]", "Reads the daily stored CI report, as-of age and coverage. MCP twin: ci_report."],
|
|
397
397
|
],
|
|
398
|
+
[
|
|
399
|
+
"delivery",
|
|
400
|
+
[
|
|
401
|
+
"Usage: cockpit delivery report [--days <1-30>] [--end <YYYY-MM-DD>] [--review-minutes <1-240>] [--json]",
|
|
402
|
+
"Delivery scoreboard: two equal periods (before and after, default 7 days each, ending before --end, default today UTC) of merged PRs split gated and ungated, ready-to-merged median and p90, main CI red time, failed production builds, coverage and the ruleset as GitHub returned it.",
|
|
403
|
+
"--review-minutes is an assumption for the review-hours line, not a measurement. Counts show their denominators; no verdict is given. MCP twin: delivery_read. Web: TEAM > Delivery.",
|
|
404
|
+
],
|
|
405
|
+
],
|
|
398
406
|
[
|
|
399
407
|
"ops",
|
|
400
408
|
[
|
|
@@ -53,6 +53,7 @@ export const rootCommandNames = new Set([
|
|
|
53
53
|
"models",
|
|
54
54
|
"scout",
|
|
55
55
|
"ci",
|
|
56
|
+
"delivery",
|
|
56
57
|
"ops",
|
|
57
58
|
"slack",
|
|
58
59
|
"settings",
|
|
@@ -121,6 +122,7 @@ const COMMAND_SUMMARY_LINES = [
|
|
|
121
122
|
" cockpit models [list|show <provider:model>|compare <provider:model> <provider:model> [...]] [--highlight] [--dashboard-url <url>] [--json]",
|
|
122
123
|
" cockpit scout [board|start|dismiss|undo <experiment-id>] [--days <n>] [--dashboard-url <url>] [--json]",
|
|
123
124
|
" cockpit ci report [--repo <owner/name>] [--json]",
|
|
125
|
+
" cockpit delivery report [--days <1-30>] [--end <YYYY-MM-DD>] [--review-minutes <1-240>] [--json]",
|
|
124
126
|
" cockpit ops [status [--job <id>] [--skips] [--memory] [--models] | recompile --person <email|name|id> [--dry-run]] [--dashboard-url <url>] [--json]",
|
|
125
127
|
" cockpit slack [connect [--workspace bli|blue_pearl] | disconnect --workspace bli|blue_pearl | coverage [--workspace bli|blue_pearl] [--stale-only] [--list] | read [--person <p>] [--channel <c>] [--query <text> [--word]] [--since <YYYY-MM-DD>] [--until <YYYY-MM-DD>] [--limit <n>] [--format compact|full]] [--json]",
|
|
126
128
|
" cockpit settings [personal [--chat-model <key>] [--brief-model <key>] | switches [set <key> <value>] | models [set --chat <key>] [--memory <id>] | cli-floor [<version>] | env list|set --project <p> --file <f> --content-stdin|delete --id <uuid> [--yes]] [--json]",
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { runScout } from "./scout.js";
|
|
2
2
|
import { runCi } from "./ci.js";
|
|
3
|
+
import { runDelivery } from "./delivery.js";
|
|
3
4
|
import { runOps } from "./ops.js";
|
|
4
5
|
/** Dispatch the operational report commands. */
|
|
5
6
|
export function runOpsCommand(command, io) {
|
|
6
7
|
switch (command.kind) {
|
|
7
8
|
case "scout": return runScout(command, io);
|
|
8
9
|
case "ci": return runCi(command, io);
|
|
10
|
+
case "delivery": return runDelivery(command, io);
|
|
9
11
|
case "ops": return runOps(command, io);
|
|
10
12
|
}
|
|
11
13
|
}
|
package/dist/commands/local.js
CHANGED
|
@@ -187,6 +187,7 @@ async function runTowerCommand(command, io) {
|
|
|
187
187
|
case "models": return await runModels(command, io);
|
|
188
188
|
case "scout":
|
|
189
189
|
case "ci":
|
|
190
|
+
case "delivery":
|
|
190
191
|
case "ops":
|
|
191
192
|
return await runOpsCommand(command, io);
|
|
192
193
|
case "slack": return await runSlack(command, io);
|
|
@@ -21,7 +21,7 @@ export async function runCockpitCli(argv, io) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
if (command === "--version" || command === "-V" || command === "version") {
|
|
24
|
-
writeLine(io?.stdout ?? process.stdout, "0.2.
|
|
24
|
+
writeLine(io?.stdout ?? process.stdout, "0.2.246");
|
|
25
25
|
return 0;
|
|
26
26
|
}
|
|
27
27
|
|
package/dist/staging-gate.js
CHANGED
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
*
|
|
16
16
|
* Three reasons close the gate, most urgent first:
|
|
17
17
|
*
|
|
18
|
-
* - `low_disk`: the volume's free space is under max(5 GiB, 5% of the volume)
|
|
18
|
+
* - `low_disk`: the volume's free space is under max(5 GiB, 5% of the volume),
|
|
19
|
+
* for a file bigger than 1% of the free space left (smaller files are admitted).
|
|
19
20
|
* - `state_over_ceiling`: everything the collector writes under its state dir
|
|
20
21
|
* (staging, spool, logs, cursors, state; never the study folders
|
|
21
22
|
* `state-dir-audit.ts` recognises) would pass the hard ceiling, 512 MiB by
|
|
@@ -43,6 +44,12 @@ export const STATE_CEILING_ENV = "COCKPIT_STATE_CEILING_BYTES";
|
|
|
43
44
|
export const MIN_FREE_ENV = "COCKPIT_MIN_FREE_BYTES";
|
|
44
45
|
export const LOW_DISK_FLOOR_BYTES = 5 * 1024 * 1024 * 1024;
|
|
45
46
|
export const LOW_DISK_FLOOR_FRACTION = 0.05;
|
|
47
|
+
/**
|
|
48
|
+
* A file at most this fraction of the free space left is never refused for low
|
|
49
|
+
* disk: a 7 MB upload does not threaten a volume with gigabytes free, even one
|
|
50
|
+
* under the floor. Only a copy that would eat a real share of what is left waits.
|
|
51
|
+
*/
|
|
52
|
+
export const LOW_DISK_SMALL_FILE_FRACTION = 0.01;
|
|
46
53
|
/**
|
|
47
54
|
* Each pack's `manifest.json` is staged beside the payload and its size grows
|
|
48
55
|
* with the gap ledger, so it is not known when a file is admitted. A tenth of
|
|
@@ -174,9 +181,22 @@ export function admitToStaging(allowance, byteLength, options = {}) {
|
|
|
174
181
|
const spent = allowance.admitted_bytes;
|
|
175
182
|
const stagedNow = snapshot.staging_bytes + spent;
|
|
176
183
|
let reason = null;
|
|
177
|
-
|
|
184
|
+
const freeNow = snapshot.free_bytes === null ? null : snapshot.free_bytes - spent;
|
|
185
|
+
const belowFloor = freeNow !== null &&
|
|
178
186
|
snapshot.floor_bytes !== null &&
|
|
179
|
-
|
|
187
|
+
freeNow - byteLength < snapshot.floor_bytes;
|
|
188
|
+
const smallForFree = freeNow !== null && freeNow > 0 && byteLength <= freeNow * LOW_DISK_SMALL_FILE_FRACTION;
|
|
189
|
+
if (belowFloor && smallForFree) {
|
|
190
|
+
console.error("[staging-gate] low-disk floor waived, file is under 1% of free space", JSON.stringify({
|
|
191
|
+
event: STAGING_DEFERRED,
|
|
192
|
+
reason: "low_disk_waived_small_file",
|
|
193
|
+
kind: options.kind ?? null,
|
|
194
|
+
file_bytes: byteLength,
|
|
195
|
+
free_bytes: freeNow,
|
|
196
|
+
floor_bytes: snapshot.floor_bytes,
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
if (belowFloor && !smallForFree) {
|
|
180
200
|
reason = "low_disk";
|
|
181
201
|
}
|
|
182
202
|
else if (snapshot.collector_bytes + spent + byteLength > snapshot.ceiling_bytes) {
|
|
@@ -202,6 +222,7 @@ export function admitToStaging(allowance, byteLength, options = {}) {
|
|
|
202
222
|
reason,
|
|
203
223
|
kind: options.kind ?? null,
|
|
204
224
|
file_bytes: byteLength,
|
|
225
|
+
needed_bytes: byteLength,
|
|
205
226
|
staging_bytes: snapshot.staging_bytes,
|
|
206
227
|
collector_bytes: snapshot.collector_bytes,
|
|
207
228
|
free_bytes: snapshot.free_bytes,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bli-cockpit/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.246",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@bli-cockpit/memory-mcp": "0.1.72",
|
|
31
|
-
"@bli-cockpit/mcp": "0.1.
|
|
31
|
+
"@bli-cockpit/mcp": "0.1.151",
|
|
32
32
|
"@bli-cockpit/telemetry-core": "0.1.79"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|