@bli-cockpit/cli 0.2.237 → 0.2.238
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-overlay.js +73 -30
- package/dist/commands/cal.js +36 -23
- package/dist/commands/clean.js +4 -1
- package/dist/commands/docs-read.js +5 -2
- 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-write.js +70 -23
- package/dist/commands/issue.js +68 -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 +52 -50
- package/dist/commands/local-args-tower-careers.js +73 -54
- package/dist/commands/local-args-tower-docs-msg.js +55 -45
- 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-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-work.js +52 -33
- 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 +6 -2
- 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/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/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 +91 -67
- 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/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 +2 -2
|
@@ -13,46 +13,45 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { DEFAULT_DASHBOARD_URL } from "../local-state.js";
|
|
15
15
|
import { assertNoPositionals, normalizeUrl, optionalEmail, optionalNonEmpty, optionalNonEmptyList, optionalPositiveInteger, optionalUrl, parseNamedArgs, workRootFlagValue, workRootFlagValues, } from "./local-arg-values.js";
|
|
16
|
+
const ONBOARD_ALLOWED_FLAGS = [
|
|
17
|
+
"--home",
|
|
18
|
+
"--repo",
|
|
19
|
+
"--workspace",
|
|
20
|
+
"--dashboard-url",
|
|
21
|
+
"--email",
|
|
22
|
+
"--device-name",
|
|
23
|
+
"--ticket",
|
|
24
|
+
"--branch",
|
|
25
|
+
"--json",
|
|
26
|
+
"--no-auth",
|
|
27
|
+
"--poll-interval-ms",
|
|
28
|
+
"--timeout-ms",
|
|
29
|
+
"--max-depth",
|
|
30
|
+
"--max-repos",
|
|
31
|
+
"--allow-home-root",
|
|
32
|
+
// BLI-3731, the one-sign-in ceremony. Same three flags as `login`, so a
|
|
33
|
+
// person who learned them on one door does not relearn them on another.
|
|
34
|
+
"--pair",
|
|
35
|
+
"--no-browser",
|
|
36
|
+
"--legacy-pair",
|
|
37
|
+
];
|
|
38
|
+
const ONBOARD_VALUE_FLAGS = [
|
|
39
|
+
"--home",
|
|
40
|
+
"--repo",
|
|
41
|
+
"--workspace",
|
|
42
|
+
"--dashboard-url",
|
|
43
|
+
"--email",
|
|
44
|
+
"--device-name",
|
|
45
|
+
"--ticket",
|
|
46
|
+
"--branch",
|
|
47
|
+
"--poll-interval-ms",
|
|
48
|
+
"--timeout-ms",
|
|
49
|
+
"--max-depth",
|
|
50
|
+
"--max-repos",
|
|
51
|
+
"--pair",
|
|
52
|
+
];
|
|
16
53
|
function parseOnboardLikeArgs(args, command) {
|
|
17
|
-
const values = parseNamedArgs(args, {
|
|
18
|
-
allowedFlags: [
|
|
19
|
-
"--home",
|
|
20
|
-
"--repo",
|
|
21
|
-
"--workspace",
|
|
22
|
-
"--dashboard-url",
|
|
23
|
-
"--email",
|
|
24
|
-
"--device-name",
|
|
25
|
-
"--ticket",
|
|
26
|
-
"--branch",
|
|
27
|
-
"--json",
|
|
28
|
-
"--no-auth",
|
|
29
|
-
"--poll-interval-ms",
|
|
30
|
-
"--timeout-ms",
|
|
31
|
-
"--max-depth",
|
|
32
|
-
"--max-repos",
|
|
33
|
-
"--allow-home-root",
|
|
34
|
-
// BLI-3731, the one-sign-in ceremony. Same three flags as `login`, so a
|
|
35
|
-
// person who learned them on one door does not relearn them on another.
|
|
36
|
-
"--pair",
|
|
37
|
-
"--no-browser",
|
|
38
|
-
"--legacy-pair",
|
|
39
|
-
],
|
|
40
|
-
valueFlags: [
|
|
41
|
-
"--home",
|
|
42
|
-
"--repo",
|
|
43
|
-
"--workspace",
|
|
44
|
-
"--dashboard-url",
|
|
45
|
-
"--email",
|
|
46
|
-
"--device-name",
|
|
47
|
-
"--ticket",
|
|
48
|
-
"--branch",
|
|
49
|
-
"--poll-interval-ms",
|
|
50
|
-
"--timeout-ms",
|
|
51
|
-
"--max-depth",
|
|
52
|
-
"--max-repos",
|
|
53
|
-
"--pair",
|
|
54
|
-
],
|
|
55
|
-
});
|
|
54
|
+
const values = parseNamedArgs(args, { allowedFlags: ONBOARD_ALLOWED_FLAGS, valueFlags: ONBOARD_VALUE_FLAGS });
|
|
56
55
|
assertNoPositionals(values.positionals, command);
|
|
57
56
|
return {
|
|
58
57
|
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
@@ -86,55 +85,54 @@ export function parseUpdateArgs(alias, args) {
|
|
|
86
85
|
...parseOnboardLikeArgs(args, alias),
|
|
87
86
|
};
|
|
88
87
|
}
|
|
88
|
+
const DOCTOR_ALLOWED_FLAGS = [
|
|
89
|
+
"--home",
|
|
90
|
+
"--repo",
|
|
91
|
+
"--workspace",
|
|
92
|
+
"--dashboard-url",
|
|
93
|
+
"--update-tag",
|
|
94
|
+
"--dry-run",
|
|
95
|
+
"--check",
|
|
96
|
+
"--no-repair",
|
|
97
|
+
"--no-usage-telemetry",
|
|
98
|
+
"--lock-wait",
|
|
99
|
+
"--backfill-budget",
|
|
100
|
+
"--json",
|
|
101
|
+
"--allow-home-root",
|
|
102
|
+
"--max-depth",
|
|
103
|
+
"--max-repos",
|
|
104
|
+
// Accepted and ignored: the convergence run works these out itself.
|
|
105
|
+
// Rejecting them would break existing DMs and runbooks for no gain.
|
|
106
|
+
"--email",
|
|
107
|
+
"--device-name",
|
|
108
|
+
"--ticket",
|
|
109
|
+
"--branch",
|
|
110
|
+
"--no-auth",
|
|
111
|
+
"--poll-interval-ms",
|
|
112
|
+
"--timeout-ms",
|
|
113
|
+
];
|
|
114
|
+
const DOCTOR_VALUE_FLAGS = [
|
|
115
|
+
"--lock-wait",
|
|
116
|
+
"--backfill-budget",
|
|
117
|
+
"--home",
|
|
118
|
+
"--repo",
|
|
119
|
+
"--workspace",
|
|
120
|
+
"--dashboard-url",
|
|
121
|
+
"--update-tag",
|
|
122
|
+
"--max-depth",
|
|
123
|
+
"--max-repos",
|
|
124
|
+
"--email",
|
|
125
|
+
"--device-name",
|
|
126
|
+
"--ticket",
|
|
127
|
+
"--branch",
|
|
128
|
+
"--poll-interval-ms",
|
|
129
|
+
"--timeout-ms",
|
|
130
|
+
];
|
|
89
131
|
export function parseDoctorArgs(alias, args) {
|
|
90
132
|
// The allowed set is the UNION of what the six setup doors used to accept.
|
|
91
133
|
// An alias that silently rejected a flag its own docs told people to pass
|
|
92
134
|
// would be a worse dead end than the one we are removing.
|
|
93
|
-
const values = parseNamedArgs(args, {
|
|
94
|
-
allowedFlags: [
|
|
95
|
-
"--home",
|
|
96
|
-
"--repo",
|
|
97
|
-
"--workspace",
|
|
98
|
-
"--dashboard-url",
|
|
99
|
-
"--update-tag",
|
|
100
|
-
"--dry-run",
|
|
101
|
-
"--check",
|
|
102
|
-
"--no-repair",
|
|
103
|
-
"--no-usage-telemetry",
|
|
104
|
-
"--lock-wait",
|
|
105
|
-
"--backfill-budget",
|
|
106
|
-
"--json",
|
|
107
|
-
"--allow-home-root",
|
|
108
|
-
"--max-depth",
|
|
109
|
-
"--max-repos",
|
|
110
|
-
// Accepted and ignored: the convergence run works these out itself.
|
|
111
|
-
// Rejecting them would break existing DMs and runbooks for no gain.
|
|
112
|
-
"--email",
|
|
113
|
-
"--device-name",
|
|
114
|
-
"--ticket",
|
|
115
|
-
"--branch",
|
|
116
|
-
"--no-auth",
|
|
117
|
-
"--poll-interval-ms",
|
|
118
|
-
"--timeout-ms",
|
|
119
|
-
],
|
|
120
|
-
valueFlags: [
|
|
121
|
-
"--lock-wait",
|
|
122
|
-
"--backfill-budget",
|
|
123
|
-
"--home",
|
|
124
|
-
"--repo",
|
|
125
|
-
"--workspace",
|
|
126
|
-
"--dashboard-url",
|
|
127
|
-
"--update-tag",
|
|
128
|
-
"--max-depth",
|
|
129
|
-
"--max-repos",
|
|
130
|
-
"--email",
|
|
131
|
-
"--device-name",
|
|
132
|
-
"--ticket",
|
|
133
|
-
"--branch",
|
|
134
|
-
"--poll-interval-ms",
|
|
135
|
-
"--timeout-ms",
|
|
136
|
-
],
|
|
137
|
-
});
|
|
135
|
+
const values = parseNamedArgs(args, { allowedFlags: DOCTOR_ALLOWED_FLAGS, valueFlags: DOCTOR_VALUE_FLAGS });
|
|
138
136
|
assertNoPositionals(values.positionals, alias);
|
|
139
137
|
const updateTag = optionalNonEmpty(values.flags.get("--update-tag"));
|
|
140
138
|
if (updateTag && !/^[a-z0-9][a-z0-9._-]*$/u.test(updateTag)) {
|
|
@@ -9,45 +9,44 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { IntentSourceSchema, WorkIntentSchema, WorkPhaseSchema, } from "@bli-cockpit/telemetry-core";
|
|
11
11
|
import { assertNoPositionals, optionalConfidence, optionalNonEmpty, optionalNonNegativeInteger, optionalPositiveInteger, optionalSchemaValue, optionalUrl, parseNamedArgs, workRootFlagValue, } from "./local-arg-values.js";
|
|
12
|
+
const START_ALLOWED_FLAGS = [
|
|
13
|
+
"--home",
|
|
14
|
+
"--repo",
|
|
15
|
+
"--workspace",
|
|
16
|
+
"--branch",
|
|
17
|
+
"--ticket",
|
|
18
|
+
"--clear-ticket",
|
|
19
|
+
"--topic",
|
|
20
|
+
"--topic-summary",
|
|
21
|
+
"--intent",
|
|
22
|
+
"--phase",
|
|
23
|
+
"--intent-source",
|
|
24
|
+
"--intent-confidence",
|
|
25
|
+
"--operator-id",
|
|
26
|
+
"--session-id",
|
|
27
|
+
"--json",
|
|
28
|
+
"--max-depth",
|
|
29
|
+
"--max-repos",
|
|
30
|
+
];
|
|
31
|
+
const START_VALUE_FLAGS = [
|
|
32
|
+
"--home",
|
|
33
|
+
"--repo",
|
|
34
|
+
"--workspace",
|
|
35
|
+
"--branch",
|
|
36
|
+
"--ticket",
|
|
37
|
+
"--topic",
|
|
38
|
+
"--topic-summary",
|
|
39
|
+
"--intent",
|
|
40
|
+
"--phase",
|
|
41
|
+
"--intent-source",
|
|
42
|
+
"--intent-confidence",
|
|
43
|
+
"--operator-id",
|
|
44
|
+
"--session-id",
|
|
45
|
+
"--max-depth",
|
|
46
|
+
"--max-repos",
|
|
47
|
+
];
|
|
12
48
|
export function parseStartArgs(args) {
|
|
13
|
-
const values = parseNamedArgs(args, {
|
|
14
|
-
allowedFlags: [
|
|
15
|
-
"--home",
|
|
16
|
-
"--repo",
|
|
17
|
-
"--workspace",
|
|
18
|
-
"--branch",
|
|
19
|
-
"--ticket",
|
|
20
|
-
"--clear-ticket",
|
|
21
|
-
"--topic",
|
|
22
|
-
"--topic-summary",
|
|
23
|
-
"--intent",
|
|
24
|
-
"--phase",
|
|
25
|
-
"--intent-source",
|
|
26
|
-
"--intent-confidence",
|
|
27
|
-
"--operator-id",
|
|
28
|
-
"--session-id",
|
|
29
|
-
"--json",
|
|
30
|
-
"--max-depth",
|
|
31
|
-
"--max-repos",
|
|
32
|
-
],
|
|
33
|
-
valueFlags: [
|
|
34
|
-
"--home",
|
|
35
|
-
"--repo",
|
|
36
|
-
"--workspace",
|
|
37
|
-
"--branch",
|
|
38
|
-
"--ticket",
|
|
39
|
-
"--topic",
|
|
40
|
-
"--topic-summary",
|
|
41
|
-
"--intent",
|
|
42
|
-
"--phase",
|
|
43
|
-
"--intent-source",
|
|
44
|
-
"--intent-confidence",
|
|
45
|
-
"--operator-id",
|
|
46
|
-
"--session-id",
|
|
47
|
-
"--max-depth",
|
|
48
|
-
"--max-repos",
|
|
49
|
-
],
|
|
50
|
-
});
|
|
49
|
+
const values = parseNamedArgs(args, { allowedFlags: START_ALLOWED_FLAGS, valueFlags: START_VALUE_FLAGS });
|
|
51
50
|
assertNoPositionals(values.positionals, "start");
|
|
52
51
|
const activeTicketId = optionalNonEmpty(values.flags.get("--ticket"));
|
|
53
52
|
const clearTicket = values.booleans.has("--clear-ticket");
|
|
@@ -65,6 +65,27 @@ export function parseScoutArgs(args) {
|
|
|
65
65
|
}
|
|
66
66
|
return { kind: "scout", action: rawAction, experimentRef, ...base };
|
|
67
67
|
}
|
|
68
|
+
const OPS_ALLOWED_FLAGS = [
|
|
69
|
+
"--home",
|
|
70
|
+
"--dashboard-url",
|
|
71
|
+
"--job",
|
|
72
|
+
"--skips",
|
|
73
|
+
"--memory",
|
|
74
|
+
"--memory-days",
|
|
75
|
+
// BLI-3798: the coverage half on its own, with the bucket table.
|
|
76
|
+
"--coverage",
|
|
77
|
+
// BLI-3912: one line per model — latency, tokens/s, today's spend, last
|
|
78
|
+
// probe verdict. A SECOND door like --memory, asked for only when named.
|
|
79
|
+
"--models",
|
|
80
|
+
"--turns",
|
|
81
|
+
"--tool-router",
|
|
82
|
+
// BLI-12507: the MCP's `people: true`. Per-device and per-person rows in --json.
|
|
83
|
+
"--people",
|
|
84
|
+
"--person",
|
|
85
|
+
"--dry-run",
|
|
86
|
+
"--json",
|
|
87
|
+
];
|
|
88
|
+
const OPS_VALUE_FLAGS = ["--home", "--dashboard-url", "--job", "--person", "--memory-days"];
|
|
68
89
|
/**
|
|
69
90
|
* `cockpit ops status [--job <id>] [--skips] [--memory [--memory-days N]]` and
|
|
70
91
|
* `cockpit ops recompile --person <p> [--dry-run]` (BLI-3462).
|
|
@@ -76,29 +97,7 @@ export function parseScoutArgs(args) {
|
|
|
76
97
|
* weekly or a monthly with the compile script, which does support it.
|
|
77
98
|
*/
|
|
78
99
|
export function parseOpsArgs(args) {
|
|
79
|
-
const values = parseNamedArgs(args, {
|
|
80
|
-
allowedFlags: [
|
|
81
|
-
"--home",
|
|
82
|
-
"--dashboard-url",
|
|
83
|
-
"--job",
|
|
84
|
-
"--skips",
|
|
85
|
-
"--memory",
|
|
86
|
-
"--memory-days",
|
|
87
|
-
// BLI-3798: the coverage half on its own, with the bucket table.
|
|
88
|
-
"--coverage",
|
|
89
|
-
// BLI-3912: one line per model — latency, tokens/s, today's spend, last
|
|
90
|
-
// probe verdict. A SECOND door like --memory, asked for only when named.
|
|
91
|
-
"--models",
|
|
92
|
-
"--turns",
|
|
93
|
-
"--tool-router",
|
|
94
|
-
// BLI-12507: the MCP's `people: true`. Per-device and per-person rows in --json.
|
|
95
|
-
"--people",
|
|
96
|
-
"--person",
|
|
97
|
-
"--dry-run",
|
|
98
|
-
"--json",
|
|
99
|
-
],
|
|
100
|
-
valueFlags: ["--home", "--dashboard-url", "--job", "--person", "--memory-days"],
|
|
101
|
-
});
|
|
100
|
+
const values = parseNamedArgs(args, { allowedFlags: OPS_ALLOWED_FLAGS, valueFlags: OPS_VALUE_FLAGS });
|
|
102
101
|
if (values.positionals.length > 1) {
|
|
103
102
|
throw new Error("ops accepts one action: status or recompile.");
|
|
104
103
|
}
|
|
@@ -111,36 +110,39 @@ export function parseOpsArgs(args) {
|
|
|
111
110
|
dashboardUrl: optionalUrl(values.flags.get("--dashboard-url")),
|
|
112
111
|
json: values.booleans.has("--json"),
|
|
113
112
|
};
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
kind: "ops",
|
|
131
|
-
action: "status",
|
|
132
|
-
job: coverageOnly ? "collection-coverage" : job,
|
|
133
|
-
coverage: coverageOnly,
|
|
134
|
-
skips: values.booleans.has("--skips"),
|
|
135
|
-
memory: values.booleans.has("--memory") || memoryDays !== undefined,
|
|
136
|
-
models: values.booleans.has("--models"),
|
|
137
|
-
turns: values.booleans.has("--turns"),
|
|
138
|
-
toolRouter: values.booleans.has("--tool-router"),
|
|
139
|
-
people: values.booleans.has("--people"),
|
|
140
|
-
...(memoryDays === undefined ? {} : { memoryDays: Number(memoryDays) }),
|
|
141
|
-
...base,
|
|
142
|
-
};
|
|
113
|
+
return rawAction === "status" ? parseOpsStatus(values, base) : parseOpsRecompile(values, base);
|
|
114
|
+
}
|
|
115
|
+
function parseOpsStatus(values, base) {
|
|
116
|
+
// BLI-3729: `--memory-days N` implies `--memory`, because asking for a
|
|
117
|
+
// window and getting nothing back is the silent breakage BLI-2490 forbids.
|
|
118
|
+
const memoryDays = optionalNonEmpty(values.flags.get("--memory-days"));
|
|
119
|
+
if (memoryDays !== undefined && !/^[0-9]{1,2}$/u.test(memoryDays)) {
|
|
120
|
+
throw new Error("ops --memory-days takes a whole number of days, 1 to 30.");
|
|
121
|
+
}
|
|
122
|
+
const coverageOnly = values.booleans.has("--coverage");
|
|
123
|
+
const job = optionalNonEmpty(values.flags.get("--job"));
|
|
124
|
+
if (coverageOnly && job !== undefined && job !== "collection-coverage") {
|
|
125
|
+
// Refused rather than silently preferring one: `--coverage --job
|
|
126
|
+
// daily-brief` is somebody asking two different questions in one
|
|
127
|
+
// command, and answering the wrong one is how a board loses trust.
|
|
128
|
+
throw new Error("ops --coverage IS --job collection-coverage. Pass one or the other, not a --job naming a different pipeline.");
|
|
143
129
|
}
|
|
130
|
+
return {
|
|
131
|
+
kind: "ops",
|
|
132
|
+
action: "status",
|
|
133
|
+
job: coverageOnly ? "collection-coverage" : job,
|
|
134
|
+
coverage: coverageOnly,
|
|
135
|
+
skips: values.booleans.has("--skips"),
|
|
136
|
+
memory: values.booleans.has("--memory") || memoryDays !== undefined,
|
|
137
|
+
models: values.booleans.has("--models"),
|
|
138
|
+
turns: values.booleans.has("--turns"),
|
|
139
|
+
toolRouter: values.booleans.has("--tool-router"),
|
|
140
|
+
people: values.booleans.has("--people"),
|
|
141
|
+
...(memoryDays === undefined ? {} : { memoryDays: Number(memoryDays) }),
|
|
142
|
+
...base,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function parseOpsRecompile(values, base) {
|
|
144
146
|
const person = optionalNonEmpty(values.flags.get("--person"));
|
|
145
147
|
if (!person) {
|
|
146
148
|
throw new Error("ops recompile needs --person <email|name|id>. Nothing is recompiled by default, on purpose.");
|
|
@@ -161,37 +163,36 @@ export const SLACK_WORKSPACE_KEYS = ["bli", "blue_pearl"];
|
|
|
161
163
|
* (BLI-3462).
|
|
162
164
|
*/
|
|
163
165
|
const SLACK_READ_FORMATS = ["compact", "full"];
|
|
166
|
+
const SLACK_ALLOWED_FLAGS = [
|
|
167
|
+
"--home",
|
|
168
|
+
"--dashboard-url",
|
|
169
|
+
"--workspace",
|
|
170
|
+
"--stale-only",
|
|
171
|
+
"--list",
|
|
172
|
+
"--person",
|
|
173
|
+
"--channel",
|
|
174
|
+
"--query",
|
|
175
|
+
"--word",
|
|
176
|
+
"--since",
|
|
177
|
+
"--until",
|
|
178
|
+
"--limit",
|
|
179
|
+
"--format",
|
|
180
|
+
"--json",
|
|
181
|
+
];
|
|
182
|
+
const SLACK_VALUE_FLAGS = [
|
|
183
|
+
"--home",
|
|
184
|
+
"--dashboard-url",
|
|
185
|
+
"--workspace",
|
|
186
|
+
"--person",
|
|
187
|
+
"--channel",
|
|
188
|
+
"--query",
|
|
189
|
+
"--since",
|
|
190
|
+
"--until",
|
|
191
|
+
"--limit",
|
|
192
|
+
"--format",
|
|
193
|
+
];
|
|
164
194
|
export function parseSlackArgs(args) {
|
|
165
|
-
const values = parseNamedArgs(args, {
|
|
166
|
-
allowedFlags: [
|
|
167
|
-
"--home",
|
|
168
|
-
"--dashboard-url",
|
|
169
|
-
"--workspace",
|
|
170
|
-
"--stale-only",
|
|
171
|
-
"--list",
|
|
172
|
-
"--person",
|
|
173
|
-
"--channel",
|
|
174
|
-
"--query",
|
|
175
|
-
"--word",
|
|
176
|
-
"--since",
|
|
177
|
-
"--until",
|
|
178
|
-
"--limit",
|
|
179
|
-
"--format",
|
|
180
|
-
"--json",
|
|
181
|
-
],
|
|
182
|
-
valueFlags: [
|
|
183
|
-
"--home",
|
|
184
|
-
"--dashboard-url",
|
|
185
|
-
"--workspace",
|
|
186
|
-
"--person",
|
|
187
|
-
"--channel",
|
|
188
|
-
"--query",
|
|
189
|
-
"--since",
|
|
190
|
-
"--until",
|
|
191
|
-
"--limit",
|
|
192
|
-
"--format",
|
|
193
|
-
],
|
|
194
|
-
});
|
|
195
|
+
const values = parseNamedArgs(args, { allowedFlags: SLACK_ALLOWED_FLAGS, valueFlags: SLACK_VALUE_FLAGS });
|
|
195
196
|
if (values.positionals.length > 1) {
|
|
196
197
|
throw new Error("slack accepts one action: coverage, read, connect or disconnect.");
|
|
197
198
|
}
|
|
@@ -210,32 +211,39 @@ export function parseSlackArgs(args) {
|
|
|
210
211
|
if (values.booleans.has("--list") && rawAction !== "coverage") {
|
|
211
212
|
throw new Error("--list belongs to `cockpit slack coverage`.");
|
|
212
213
|
}
|
|
213
|
-
if (rawAction === "connect" || rawAction === "disconnect")
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
// BLI-10450: counts alone could not answer "is there a Kaiben channel?".
|
|
235
|
-
list: values.booleans.has("--list"),
|
|
236
|
-
...base,
|
|
237
|
-
};
|
|
214
|
+
if (rawAction === "connect" || rawAction === "disconnect")
|
|
215
|
+
return parseSlackConnection(values, base, rawAction);
|
|
216
|
+
if (rawAction === "coverage")
|
|
217
|
+
return parseSlackCoverage(values, base);
|
|
218
|
+
return parseSlackRead(values, base);
|
|
219
|
+
}
|
|
220
|
+
function parseSlackConnection(values, base, action) {
|
|
221
|
+
const workspace = optionalNonEmpty(values.flags.get("--workspace"));
|
|
222
|
+
if (workspace && !SLACK_WORKSPACE_KEYS.includes(workspace))
|
|
223
|
+
throw new Error("Unknown Slack workspace.");
|
|
224
|
+
if (action === "disconnect" && !workspace)
|
|
225
|
+
throw new Error("slack disconnect needs --workspace bli|blue_pearl.");
|
|
226
|
+
return { kind: "slack", action, workspace, ...base };
|
|
227
|
+
}
|
|
228
|
+
function parseSlackCoverage(values, base) {
|
|
229
|
+
// `--workspace` here is a Slack workspace key, NOT the collection-root
|
|
230
|
+
// `--workspace <path>` every setup command takes. Named at the point of use
|
|
231
|
+
// so the two cannot be confused silently.
|
|
232
|
+
const workspace = optionalNonEmpty(values.flags.get("--workspace"));
|
|
233
|
+
if (workspace && !SLACK_WORKSPACE_KEYS.includes(workspace)) {
|
|
234
|
+
throw new Error(`slack --workspace must be one of ${SLACK_WORKSPACE_KEYS.join(", ")}; got ${workspace}.`);
|
|
238
235
|
}
|
|
236
|
+
return {
|
|
237
|
+
kind: "slack",
|
|
238
|
+
action: "coverage",
|
|
239
|
+
workspace,
|
|
240
|
+
staleOnly: values.booleans.has("--stale-only"),
|
|
241
|
+
// BLI-10450: counts alone could not answer "is there a Kaiben channel?".
|
|
242
|
+
list: values.booleans.has("--list"),
|
|
243
|
+
...base,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
function parseSlackRead(values, base) {
|
|
239
247
|
const person = optionalNonEmpty(values.flags.get("--person"));
|
|
240
248
|
const channel = optionalNonEmpty(values.flags.get("--channel"));
|
|
241
249
|
const query = optionalNonEmpty(values.flags.get("--query"));
|
|
@@ -271,20 +279,19 @@ export function parseSlackArgs(args) {
|
|
|
271
279
|
* `cockpit team [members|invite <email>|role <userId>|device list|device
|
|
272
280
|
* revoke <id|name>]` (BLI-3461; `device` added BLI-3559).
|
|
273
281
|
*/
|
|
282
|
+
const TEAM_ALLOWED_FLAGS = [
|
|
283
|
+
"--home",
|
|
284
|
+
"--dashboard-url",
|
|
285
|
+
"--json",
|
|
286
|
+
"--role",
|
|
287
|
+
"--team-id",
|
|
288
|
+
"--yes",
|
|
289
|
+
"--reason",
|
|
290
|
+
"--note",
|
|
291
|
+
];
|
|
292
|
+
const TEAM_VALUE_FLAGS = ["--home", "--dashboard-url", "--role", "--team-id", "--reason", "--note"];
|
|
274
293
|
export function parseTeamArgs(args) {
|
|
275
|
-
const values = parseNamedArgs(args, {
|
|
276
|
-
allowedFlags: [
|
|
277
|
-
"--home",
|
|
278
|
-
"--dashboard-url",
|
|
279
|
-
"--json",
|
|
280
|
-
"--role",
|
|
281
|
-
"--team-id",
|
|
282
|
-
"--yes",
|
|
283
|
-
"--reason",
|
|
284
|
-
"--note",
|
|
285
|
-
],
|
|
286
|
-
valueFlags: ["--home", "--dashboard-url", "--role", "--team-id", "--reason", "--note"],
|
|
287
|
-
});
|
|
294
|
+
const values = parseNamedArgs(args, { allowedFlags: TEAM_ALLOWED_FLAGS, valueFlags: TEAM_VALUE_FLAGS });
|
|
288
295
|
const common = {
|
|
289
296
|
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
290
297
|
dashboardUrl: optionalUrl(values.flags.get("--dashboard-url")),
|
|
@@ -293,31 +300,15 @@ export function parseTeamArgs(args) {
|
|
|
293
300
|
json: values.booleans.has("--json"),
|
|
294
301
|
};
|
|
295
302
|
const [verb, ...rest] = values.positionals;
|
|
296
|
-
if (verb === "github-login")
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
throw new Error("team github-login takes add|remove <person> <login>, or list <person>.");
|
|
301
|
-
}
|
|
302
|
-
return { kind: "team", action: `github-login-${operation}`, personRef, githubLogin, ...common };
|
|
303
|
-
}
|
|
304
|
-
if (verb === "timezone") {
|
|
305
|
-
const [operation, personRef, zone, ...extra] = rest;
|
|
306
|
-
if (!["set", "list"].includes(operation ?? "") || extra.length ||
|
|
307
|
-
(operation === "list" ? personRef !== undefined : !personRef || !zone)) {
|
|
308
|
-
throw new Error("team timezone takes set <person> <iana-zone> (or the word unknown), or list.");
|
|
309
|
-
}
|
|
310
|
-
return { kind: "team", action: operation === "set" ? "timezone-set" : "timezone-list", ...(personRef ? { personRef } : {}), ...(zone ? { zone } : {}), ...common };
|
|
311
|
-
}
|
|
303
|
+
if (verb === "github-login")
|
|
304
|
+
return parseTeamGithubLoginArgs(rest, common);
|
|
305
|
+
if (verb === "timezone")
|
|
306
|
+
return parseTeamTimezoneArgs(rest, common);
|
|
312
307
|
if (verb === "device") {
|
|
313
308
|
return parseTeamDeviceArgs(rest, values.flags, common);
|
|
314
309
|
}
|
|
315
|
-
if (verb === "grant" || verb === "revoke")
|
|
316
|
-
|
|
317
|
-
if (!person || section !== "careers" || !["see", "act"].includes(level) || extra.length)
|
|
318
|
-
throw new Error("team grant|revoke <person uuid> careers [see|act]");
|
|
319
|
-
return { kind: "team", action: verb, targetUserId: person, section: "careers", level: level, ...common };
|
|
320
|
-
}
|
|
310
|
+
if (verb === "grant" || verb === "revoke")
|
|
311
|
+
return parseTeamGrantArgs(verb, rest, common);
|
|
321
312
|
const [subject, ...extra] = rest;
|
|
322
313
|
if (extra.length > 0)
|
|
323
314
|
throw new Error("team does not accept that many arguments.");
|
|
@@ -345,6 +336,28 @@ export function parseTeamArgs(args) {
|
|
|
345
336
|
}
|
|
346
337
|
throw new Error("team takes one verb: members, invite, role, grant, revoke, or device.");
|
|
347
338
|
}
|
|
339
|
+
function parseTeamGithubLoginArgs(rest, common) {
|
|
340
|
+
const [operation, personRef, githubLogin, ...extra] = rest;
|
|
341
|
+
if (!["add", "remove", "list"].includes(operation ?? "") || !personRef || extra.length ||
|
|
342
|
+
(operation === "list" ? githubLogin !== undefined : !githubLogin)) {
|
|
343
|
+
throw new Error("team github-login takes add|remove <person> <login>, or list <person>.");
|
|
344
|
+
}
|
|
345
|
+
return { kind: "team", action: `github-login-${operation}`, personRef, githubLogin, ...common };
|
|
346
|
+
}
|
|
347
|
+
function parseTeamTimezoneArgs(rest, common) {
|
|
348
|
+
const [operation, personRef, zone, ...extra] = rest;
|
|
349
|
+
if (!["set", "list"].includes(operation ?? "") || extra.length ||
|
|
350
|
+
(operation === "list" ? personRef !== undefined : !personRef || !zone)) {
|
|
351
|
+
throw new Error("team timezone takes set <person> <iana-zone> (or the word unknown), or list.");
|
|
352
|
+
}
|
|
353
|
+
return { kind: "team", action: operation === "set" ? "timezone-set" : "timezone-list", ...(personRef ? { personRef } : {}), ...(zone ? { zone } : {}), ...common };
|
|
354
|
+
}
|
|
355
|
+
function parseTeamGrantArgs(verb, rest, common) {
|
|
356
|
+
const [person, section, level = "see", ...extra] = rest;
|
|
357
|
+
if (!person || section !== "careers" || !["see", "act"].includes(level) || extra.length)
|
|
358
|
+
throw new Error("team grant|revoke <person uuid> careers [see|act]");
|
|
359
|
+
return { kind: "team", action: verb, targetUserId: person, section: "careers", level: level, ...common };
|
|
360
|
+
}
|
|
348
361
|
/**
|
|
349
362
|
* `cockpit team device [list] [--json]` or
|
|
350
363
|
* `cockpit team device revoke <id|name> --reason <label> [--note "<text>"]`
|