@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
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { optionalNonEmpty, optionalPositiveInteger, optionalUrl, parseNamedArgs, } from "./local-arg-values.js";
|
|
22
22
|
const CAL_ACTIONS = new Set([
|
|
23
|
-
"conflicts", "calendar-change",
|
|
23
|
+
"event", "export", "conflicts", "calendar-change",
|
|
24
24
|
"today",
|
|
25
25
|
"week",
|
|
26
26
|
"next",
|
|
@@ -36,58 +36,59 @@ const CAL_ACTIONS = new Set([
|
|
|
36
36
|
"delete",
|
|
37
37
|
]);
|
|
38
38
|
/** Every verb that names ONE thing first — a calendar id, or (delete) an event id. */
|
|
39
|
-
const CAL_ACTIONS_NEEDING_A_SUBJECT = new Set(["calendar-change", "sync", "detach", "share", "delete", "change", "cancel"]);
|
|
39
|
+
const CAL_ACTIONS_NEEDING_A_SUBJECT = new Set(["event", "export", "calendar-change", "sync", "detach", "share", "delete", "change", "cancel"]);
|
|
40
40
|
/** `sync-status [<calendar id>]` — every calendar, or one's. Ticket 10730. */
|
|
41
41
|
const CAL_ACTIONS_WITH_OPTIONAL_SUBJECT = new Set(["sync-status"]);
|
|
42
|
+
const CAL_ALLOWED_FLAGS = [
|
|
43
|
+
"--home",
|
|
44
|
+
"--dashboard-url",
|
|
45
|
+
"--color",
|
|
46
|
+
"--calendar",
|
|
47
|
+
"--account",
|
|
48
|
+
"--tz",
|
|
49
|
+
"--occurrence-start",
|
|
50
|
+
"--offset",
|
|
51
|
+
"--hours",
|
|
52
|
+
"--from",
|
|
53
|
+
"--to",
|
|
54
|
+
"--limit",
|
|
55
|
+
"--all",
|
|
56
|
+
"--name",
|
|
57
|
+
"--title",
|
|
58
|
+
"--at",
|
|
59
|
+
"--until",
|
|
60
|
+
"--location",
|
|
61
|
+
"--attendee",
|
|
62
|
+
"--all-day",
|
|
63
|
+
"--meet",
|
|
64
|
+
"--org-visible",
|
|
65
|
+
"--private",
|
|
66
|
+
"--full",
|
|
67
|
+
"--yes",
|
|
68
|
+
"--json",
|
|
69
|
+
];
|
|
70
|
+
const CAL_VALUE_FLAGS = [
|
|
71
|
+
"--home",
|
|
72
|
+
"--dashboard-url",
|
|
73
|
+
"--color",
|
|
74
|
+
"--calendar",
|
|
75
|
+
"--account",
|
|
76
|
+
"--tz",
|
|
77
|
+
"--occurrence-start",
|
|
78
|
+
"--offset",
|
|
79
|
+
"--hours",
|
|
80
|
+
"--from",
|
|
81
|
+
"--to",
|
|
82
|
+
"--limit",
|
|
83
|
+
"--name",
|
|
84
|
+
"--title",
|
|
85
|
+
"--at",
|
|
86
|
+
"--until",
|
|
87
|
+
"--location",
|
|
88
|
+
"--attendee",
|
|
89
|
+
];
|
|
42
90
|
export function parseCalArgs(args) {
|
|
43
|
-
const values = parseNamedArgs(args, {
|
|
44
|
-
allowedFlags: [
|
|
45
|
-
"--home",
|
|
46
|
-
"--dashboard-url",
|
|
47
|
-
"--color",
|
|
48
|
-
"--calendar",
|
|
49
|
-
"--account",
|
|
50
|
-
"--tz",
|
|
51
|
-
"--offset",
|
|
52
|
-
"--hours",
|
|
53
|
-
"--from",
|
|
54
|
-
"--to",
|
|
55
|
-
"--limit",
|
|
56
|
-
"--all",
|
|
57
|
-
"--name",
|
|
58
|
-
"--title",
|
|
59
|
-
"--at",
|
|
60
|
-
"--until",
|
|
61
|
-
"--location",
|
|
62
|
-
"--attendee",
|
|
63
|
-
"--all-day",
|
|
64
|
-
"--meet",
|
|
65
|
-
"--org-visible",
|
|
66
|
-
"--private",
|
|
67
|
-
"--full",
|
|
68
|
-
"--yes",
|
|
69
|
-
"--json",
|
|
70
|
-
],
|
|
71
|
-
valueFlags: [
|
|
72
|
-
"--home",
|
|
73
|
-
"--dashboard-url",
|
|
74
|
-
"--color",
|
|
75
|
-
"--calendar",
|
|
76
|
-
"--account",
|
|
77
|
-
"--tz",
|
|
78
|
-
"--offset",
|
|
79
|
-
"--hours",
|
|
80
|
-
"--from",
|
|
81
|
-
"--to",
|
|
82
|
-
"--limit",
|
|
83
|
-
"--name",
|
|
84
|
-
"--title",
|
|
85
|
-
"--at",
|
|
86
|
-
"--until",
|
|
87
|
-
"--location",
|
|
88
|
-
"--attendee",
|
|
89
|
-
],
|
|
90
|
-
});
|
|
91
|
+
const values = parseNamedArgs(args, { allowedFlags: CAL_ALLOWED_FLAGS, valueFlags: CAL_VALUE_FLAGS });
|
|
91
92
|
const { action, rest } = readCalAction(values);
|
|
92
93
|
const { subject, query } = readCalSubjectOrQuery(action, rest);
|
|
93
94
|
const { title, startsAt, endsAt } = readCalCreateDetails(action, values);
|
|
@@ -173,6 +174,7 @@ function readCalOffset(values) {
|
|
|
173
174
|
function buildCalCommand(values, action, subject, query, title, startsAt, endsAt, offset, limit) {
|
|
174
175
|
return {
|
|
175
176
|
kind: "cal",
|
|
177
|
+
occurrenceStart: optionalNonEmpty(values.flags.get("--occurrence-start")),
|
|
176
178
|
color: optionalNonEmpty(values.flags.get("--color")),
|
|
177
179
|
action,
|
|
178
180
|
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
@@ -9,11 +9,56 @@ const NEEDS_ROLE = ['takehome-show', 'takehome-set'];
|
|
|
9
9
|
export function parseCareersArgs(args) {
|
|
10
10
|
const valueFlags = ['--role', '--min-score', '--since', '--home', '--dashboard-url', '--decision', '--stage', '--subject', '--body-file', '--link', '--pass-score', '--account', '--auto-invite-min-score', '--attention-min-score', '--enabled', '--booking-link', '--grader-model', '--out', '--record-sent-at', '--at', '--followup-enabled', '--followup-after-days', '--takehome-window-hours'];
|
|
11
11
|
const values = parseNamedArgs(args, { allowedFlags: [...valueFlags, '--json', '--now', '--force', '--clear'], valueFlags });
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
const { verb, target } = readCareersVerb([...values.positionals]);
|
|
13
|
+
const rawScore = values.flags.get('--min-score');
|
|
14
|
+
const minScore = rawScore === undefined ? undefined : Number(rawScore);
|
|
15
|
+
if (minScore !== undefined && (!Number.isFinite(minScore) || minScore < 0 || minScore > 100))
|
|
16
|
+
throw new Error('--min-score must be 0 to 100.');
|
|
17
|
+
const since = optionalNonEmpty(values.flags.get('--since'));
|
|
18
|
+
if (since && !Number.isFinite(Date.parse(since)))
|
|
19
|
+
throw new Error('--since must be a date.');
|
|
20
|
+
const decision = optionalNonEmpty(values.flags.get('--decision'));
|
|
21
|
+
if (verb === 'decide' && (!decision || !DECISIONS.includes(decision)))
|
|
22
|
+
throw new Error(`careers decide needs --decision ${DECISIONS.join('|')}.`);
|
|
23
|
+
const passScore = numberFlag(values.flags.get('--pass-score'), '--pass-score', 0, 10);
|
|
24
|
+
const autoInviteMinScore = numberFlag(values.flags.get('--auto-invite-min-score'), '--auto-invite-min-score', 0, 100);
|
|
25
|
+
const attentionMinScore = numberFlag(values.flags.get('--attention-min-score'), '--attention-min-score', 0, 100);
|
|
26
|
+
const enabledRaw = trueOrFalseFlag(values.flags.get('--enabled'), '--enabled');
|
|
27
|
+
const recordSentAt = readRecordSentAt(values, verb);
|
|
28
|
+
const { interviewAt, clear } = readInterviewFlags(values, verb);
|
|
29
|
+
const followupEnabledRaw = trueOrFalseFlag(values.flags.get('--followup-enabled'), '--followup-enabled');
|
|
30
|
+
const followupAfterDays = wholeFlag(values.flags.get('--followup-after-days'), '--followup-after-days', 1, 60);
|
|
31
|
+
const takehomeWindowHours = wholeFlag(values.flags.get('--takehome-window-hours'), '--takehome-window-hours', 1, 720);
|
|
32
|
+
const { outPath, force } = readResumeFileFlags(values, verb);
|
|
33
|
+
return {
|
|
34
|
+
kind: 'careers', action: verb,
|
|
35
|
+
id: NEEDS_ID.includes(verb) ? target : undefined,
|
|
36
|
+
takehomeRole: NEEDS_ROLE.includes(verb) ? target : undefined,
|
|
37
|
+
role: optionalNonEmpty(values.flags.get('--role')), minScore, since: since ? new Date(since).toISOString() : undefined,
|
|
38
|
+
stage: optionalNonEmpty(values.flags.get('--stage')), decision,
|
|
39
|
+
subject: optionalNonEmpty(values.flags.get('--subject')), bodyFile: optionalNonEmpty(values.flags.get('--body-file')),
|
|
40
|
+
link: optionalNonEmpty(values.flags.get('--link')), passScore,
|
|
41
|
+
inviteAccountId: optionalNonEmpty(values.flags.get('--account')),
|
|
42
|
+
bookingLink: httpsFlag(values.flags.get('--booking-link')),
|
|
43
|
+
graderModel: optionalNonEmpty(values.flags.get('--grader-model')),
|
|
44
|
+
now: values.booleans.has('--now'),
|
|
45
|
+
outPath, force,
|
|
46
|
+
interviewAt: interviewAt ? new Date(interviewAt).toISOString() : undefined, clear: clear || undefined,
|
|
47
|
+
recordSentAt: recordSentAt ? new Date(recordSentAt).toISOString() : undefined,
|
|
48
|
+
followupEnabled: followupEnabledRaw === undefined ? undefined : followupEnabledRaw === 'true',
|
|
49
|
+
followupAfterDays, takehomeWindowHours,
|
|
50
|
+
autoInviteMinScore, attentionMinScore,
|
|
51
|
+
autoInviteEnabled: enabledRaw === undefined ? undefined : enabledRaw === 'true',
|
|
52
|
+
homeDir: optionalNonEmpty(values.flags.get('--home')), dashboardUrl: optionalUrl(values.flags.get('--dashboard-url')), json: values.booleans.has('--json'),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* `takehome show` and `settings set` are two words a person types and one
|
|
57
|
+
* action everything downstream reads.
|
|
58
|
+
* A bare `careers takehome` or `careers settings` reads as its `show`, the way
|
|
59
|
+
* `team device` reads as `team device list`: the noun alone is a question.
|
|
60
|
+
*/
|
|
61
|
+
function readCareersVerb(positionals) {
|
|
17
62
|
const first = positionals[0] ?? 'list';
|
|
18
63
|
const paired = first === 'takehome' || first === 'settings';
|
|
19
64
|
const typed = paired
|
|
@@ -32,32 +77,28 @@ export function parseCareersArgs(args) {
|
|
|
32
77
|
throw new Error(`careers ${verb.replace('-', ' ')} needs a role slug.`);
|
|
33
78
|
if (positionals.length > 0)
|
|
34
79
|
throw new Error('Unexpected careers argument.');
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const attentionMinScore = numberFlag(values.flags.get('--attention-min-score'), '--attention-min-score', 0, 100);
|
|
48
|
-
const enabledRaw = optionalNonEmpty(values.flags.get('--enabled'));
|
|
49
|
-
if (enabledRaw !== undefined && enabledRaw !== 'true' && enabledRaw !== 'false')
|
|
50
|
-
throw new Error('--enabled must be true or false.');
|
|
51
|
-
// BLI-12183: the second-chance follow-up. `--record-sent-at` records one sent
|
|
52
|
-
// outside Tower and sends nothing; the three settings flags are whole numbers
|
|
53
|
-
// and a boolean, refused here as well as on the door.
|
|
80
|
+
return { typed, verb, target };
|
|
81
|
+
}
|
|
82
|
+
function trueOrFalseFlag(raw, flag) {
|
|
83
|
+
const value = optionalNonEmpty(raw);
|
|
84
|
+
if (value !== undefined && value !== 'true' && value !== 'false')
|
|
85
|
+
throw new Error(`${flag} must be true or false.`);
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
// BLI-12183: the second-chance follow-up. `--record-sent-at` records one sent
|
|
89
|
+
// outside Tower and sends nothing; the three settings flags are whole numbers
|
|
90
|
+
// and a boolean, refused here as well as on the door.
|
|
91
|
+
function readRecordSentAt(values, verb) {
|
|
54
92
|
const recordSentAt = optionalNonEmpty(values.flags.get('--record-sent-at'));
|
|
55
93
|
if (recordSentAt !== undefined && verb !== 'followup')
|
|
56
94
|
throw new Error('--record-sent-at belongs to `cockpit careers followup <id>`.');
|
|
57
95
|
if (recordSentAt !== undefined && !Number.isFinite(Date.parse(recordSentAt)))
|
|
58
96
|
throw new Error('--record-sent-at must be an ISO date-time, e.g. 2026-10-03T08:00:00Z.');
|
|
59
|
-
|
|
60
|
-
|
|
97
|
+
return recordSentAt;
|
|
98
|
+
}
|
|
99
|
+
// BLI-12198: an interview Edward booked himself. Exactly one of `--at <iso>`
|
|
100
|
+
// (book or reschedule) or `--clear`, refused here as well as on the door.
|
|
101
|
+
function readInterviewFlags(values, verb) {
|
|
61
102
|
const interviewAt = optionalNonEmpty(values.flags.get('--at'));
|
|
62
103
|
const clear = values.booleans.has('--clear');
|
|
63
104
|
if (interviewAt !== undefined && verb !== 'interview')
|
|
@@ -71,13 +112,11 @@ export function parseCareersArgs(args) {
|
|
|
71
112
|
throw new Error('--at must be an ISO date-time with an offset, e.g. 2026-10-05T10:00:00-07:00.');
|
|
72
113
|
}
|
|
73
114
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// The resume is the only thing this noun writes to disk, so both file flags
|
|
80
|
-
// belong to it alone and say so rather than being accepted and ignored.
|
|
115
|
+
return { interviewAt, clear };
|
|
116
|
+
}
|
|
117
|
+
// The resume is the only thing this noun writes to disk, so both file flags
|
|
118
|
+
// belong to it alone and say so rather than being accepted and ignored.
|
|
119
|
+
function readResumeFileFlags(values, verb) {
|
|
81
120
|
const outPath = optionalNonEmpty(values.flags.get('--out'));
|
|
82
121
|
const force = values.booleans.has('--force');
|
|
83
122
|
if (outPath !== undefined && verb !== 'resume')
|
|
@@ -86,27 +125,7 @@ export function parseCareersArgs(args) {
|
|
|
86
125
|
throw new Error('--force belongs to `cockpit careers resume <id> --out <path>`.');
|
|
87
126
|
if (force && outPath === undefined)
|
|
88
127
|
throw new Error('--force only means something beside --out <path>: with no file to write there is nothing to overwrite.');
|
|
89
|
-
return {
|
|
90
|
-
kind: 'careers', action: verb,
|
|
91
|
-
id: NEEDS_ID.includes(verb) ? target : undefined,
|
|
92
|
-
takehomeRole: NEEDS_ROLE.includes(verb) ? target : undefined,
|
|
93
|
-
role: optionalNonEmpty(values.flags.get('--role')), minScore, since: since ? new Date(since).toISOString() : undefined,
|
|
94
|
-
stage: optionalNonEmpty(values.flags.get('--stage')), decision,
|
|
95
|
-
subject: optionalNonEmpty(values.flags.get('--subject')), bodyFile: optionalNonEmpty(values.flags.get('--body-file')),
|
|
96
|
-
link: optionalNonEmpty(values.flags.get('--link')), passScore,
|
|
97
|
-
inviteAccountId: optionalNonEmpty(values.flags.get('--account')),
|
|
98
|
-
bookingLink: httpsFlag(values.flags.get('--booking-link')),
|
|
99
|
-
graderModel: optionalNonEmpty(values.flags.get('--grader-model')),
|
|
100
|
-
now: values.booleans.has('--now'),
|
|
101
|
-
outPath, force,
|
|
102
|
-
interviewAt: interviewAt ? new Date(interviewAt).toISOString() : undefined, clear: clear || undefined,
|
|
103
|
-
recordSentAt: recordSentAt ? new Date(recordSentAt).toISOString() : undefined,
|
|
104
|
-
followupEnabled: followupEnabledRaw === undefined ? undefined : followupEnabledRaw === 'true',
|
|
105
|
-
followupAfterDays, takehomeWindowHours,
|
|
106
|
-
autoInviteMinScore, attentionMinScore,
|
|
107
|
-
autoInviteEnabled: enabledRaw === undefined ? undefined : enabledRaw === 'true',
|
|
108
|
-
homeDir: optionalNonEmpty(values.flags.get('--home')), dashboardUrl: optionalUrl(values.flags.get('--dashboard-url')), json: values.booleans.has('--json'),
|
|
109
|
-
};
|
|
128
|
+
return { outPath, force };
|
|
110
129
|
}
|
|
111
130
|
/**
|
|
112
131
|
* BLI-4461: a booking link goes into an email a stranger clicks, so the
|
|
@@ -15,44 +15,43 @@ const DOCS_ACTIONS = new Set(["list", "tree", "read", "create", "update", "delet
|
|
|
15
15
|
// (the same door, called once per ref), so it joined `read`'s branch rather
|
|
16
16
|
// than this set.
|
|
17
17
|
const DOCS_ACTIONS_NEEDING_A_DOC = new Set(["update", "link", "export"]);
|
|
18
|
+
const DOCS_ALLOWED_FLAGS = [
|
|
19
|
+
"--home",
|
|
20
|
+
"--dashboard-url",
|
|
21
|
+
"--title",
|
|
22
|
+
"--parent",
|
|
23
|
+
"--clear-parent",
|
|
24
|
+
"--allow-empty",
|
|
25
|
+
"--visibility",
|
|
26
|
+
"--file",
|
|
27
|
+
"--body-stdin",
|
|
28
|
+
"--query",
|
|
29
|
+
"--word",
|
|
30
|
+
"--limit",
|
|
31
|
+
"--clear",
|
|
32
|
+
"--bodies",
|
|
33
|
+
"--section",
|
|
34
|
+
"--offset",
|
|
35
|
+
"--format",
|
|
36
|
+
"--out",
|
|
37
|
+
"--json",
|
|
38
|
+
];
|
|
39
|
+
const DOCS_VALUE_FLAGS = [
|
|
40
|
+
"--home",
|
|
41
|
+
"--dashboard-url",
|
|
42
|
+
"--title",
|
|
43
|
+
"--parent",
|
|
44
|
+
"--visibility",
|
|
45
|
+
"--file",
|
|
46
|
+
"--query",
|
|
47
|
+
"--limit",
|
|
48
|
+
"--section",
|
|
49
|
+
"--offset",
|
|
50
|
+
"--format",
|
|
51
|
+
"--out",
|
|
52
|
+
];
|
|
18
53
|
export function parseDocsArgs(args) {
|
|
19
|
-
const values = parseNamedArgs(args, {
|
|
20
|
-
allowedFlags: [
|
|
21
|
-
"--home",
|
|
22
|
-
"--dashboard-url",
|
|
23
|
-
"--title",
|
|
24
|
-
"--parent",
|
|
25
|
-
"--clear-parent",
|
|
26
|
-
"--allow-empty",
|
|
27
|
-
"--visibility",
|
|
28
|
-
"--file",
|
|
29
|
-
"--body-stdin",
|
|
30
|
-
"--query",
|
|
31
|
-
"--word",
|
|
32
|
-
"--limit",
|
|
33
|
-
"--clear",
|
|
34
|
-
"--bodies",
|
|
35
|
-
"--section",
|
|
36
|
-
"--offset",
|
|
37
|
-
"--format",
|
|
38
|
-
"--out",
|
|
39
|
-
"--json",
|
|
40
|
-
],
|
|
41
|
-
valueFlags: [
|
|
42
|
-
"--home",
|
|
43
|
-
"--dashboard-url",
|
|
44
|
-
"--title",
|
|
45
|
-
"--parent",
|
|
46
|
-
"--visibility",
|
|
47
|
-
"--file",
|
|
48
|
-
"--query",
|
|
49
|
-
"--limit",
|
|
50
|
-
"--section",
|
|
51
|
-
"--offset",
|
|
52
|
-
"--format",
|
|
53
|
-
"--out",
|
|
54
|
-
],
|
|
55
|
-
});
|
|
54
|
+
const values = parseNamedArgs(args, { allowedFlags: DOCS_ALLOWED_FLAGS, valueFlags: DOCS_VALUE_FLAGS });
|
|
56
55
|
const { action, rest } = parseDocsAction(values.positionals);
|
|
57
56
|
const link = parseDocsLink(action, rest, values.booleans.has("--clear"));
|
|
58
57
|
const { docRef, docRefs } = parseDocsDocumentReference(action, link.rest);
|
|
@@ -150,6 +149,10 @@ function parseDocsDocumentReference(action, rest) {
|
|
|
150
149
|
return { docRef: undefined, docRefs: undefined };
|
|
151
150
|
}
|
|
152
151
|
function parseDocsFlags(action, values) {
|
|
152
|
+
return { ...parseDocsWriteFlags(action, values), ...parseDocsReadFlags(action, values) };
|
|
153
|
+
}
|
|
154
|
+
/** The flags that decide where a document lives and what it is called. */
|
|
155
|
+
function parseDocsWriteFlags(action, values) {
|
|
153
156
|
const visibility = optionalNonEmpty(values.flags.get("--visibility"));
|
|
154
157
|
if (visibility && visibility !== "org" && visibility !== "private") {
|
|
155
158
|
throw new Error('docs --visibility must be "org" or "private".');
|
|
@@ -174,6 +177,10 @@ function parseDocsFlags(action, values) {
|
|
|
174
177
|
if (action === "create" && !title) {
|
|
175
178
|
throw new Error("docs create needs --title.");
|
|
176
179
|
}
|
|
180
|
+
return { visibility, clearParent, parentId, allowEmpty, title };
|
|
181
|
+
}
|
|
182
|
+
/** The flags that narrow or slice what a list or read returns. */
|
|
183
|
+
function parseDocsReadFlags(action, values) {
|
|
177
184
|
// BLI-3737: narrowing belongs to `list` only. `--parent` keeps its create/
|
|
178
185
|
// update meaning elsewhere (which parent to file the document under); on
|
|
179
186
|
// `list` the same word names which parent to list UNDER, plus the literal
|
|
@@ -205,16 +212,19 @@ function parseDocsFlags(action, values) {
|
|
|
205
212
|
const section = optionalNonEmpty(values.flags.get("--section"));
|
|
206
213
|
if (section && action !== "read")
|
|
207
214
|
throw new Error("--section belongs to `cockpit docs read`.");
|
|
208
|
-
const
|
|
209
|
-
let offset;
|
|
210
|
-
if (offsetRaw !== undefined) {
|
|
211
|
-
offset = Number(offsetRaw);
|
|
212
|
-
if (!Number.isInteger(offset) || offset < 0)
|
|
213
|
-
throw new Error("--offset must be a whole number 0 or greater.");
|
|
214
|
-
}
|
|
215
|
+
const offset = parseDocsOffset(values.flags.get("--offset"));
|
|
215
216
|
if (offset !== undefined && action !== "read")
|
|
216
217
|
throw new Error("--offset belongs to `cockpit docs read`.");
|
|
217
|
-
return {
|
|
218
|
+
return { query, wholeWord, limit, bodies, section, offset };
|
|
219
|
+
}
|
|
220
|
+
function parseDocsOffset(raw) {
|
|
221
|
+
const offsetRaw = optionalNonEmpty(raw);
|
|
222
|
+
if (offsetRaw === undefined)
|
|
223
|
+
return undefined;
|
|
224
|
+
const offset = Number(offsetRaw);
|
|
225
|
+
if (!Number.isInteger(offset) || offset < 0)
|
|
226
|
+
throw new Error("--offset must be a whole number 0 or greater.");
|
|
227
|
+
return offset;
|
|
218
228
|
}
|
|
219
229
|
function buildDocsCommand(values, action, docRef, docRefs, visibility, clearParent, parentId, allowEmpty, title, query, wholeWord, limit, bodies, section, offset, link) {
|
|
220
230
|
return {
|
|
@@ -33,26 +33,8 @@ export function parseGithubArgs(args) {
|
|
|
33
33
|
throw new Error(`github ${action} takes a repo and a pull request number only.`);
|
|
34
34
|
const pullNumber = numberText === undefined ? undefined : optionalPositiveInteger(numberText, "pull request number");
|
|
35
35
|
const runId = optionalPositiveInteger(values.flags.get("--run"), "--run");
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
throw new Error("github rerun needs exactly one of a pull request number or --run <id>.");
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
else if (pullNumber === undefined) {
|
|
42
|
-
throw new Error(`github ${action} needs a pull request number.`);
|
|
43
|
-
}
|
|
44
|
-
const only = (flag, allowed) => {
|
|
45
|
-
if (values.flags.has(flag) && !allowed.includes(action)) {
|
|
46
|
-
throw new Error(`${flag} belongs to \`cockpit github ${allowed.join("|")}\`.`);
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
only("--claim", ["pr"]);
|
|
50
|
-
only("--method", ["merge"]);
|
|
51
|
-
only("--title", ["retitle"]);
|
|
52
|
-
only("--run", ["rerun"]);
|
|
53
|
-
if (values.flags.has("--confirm") && action === "pr") {
|
|
54
|
-
throw new Error("--confirm belongs to merge, retitle and rerun; pr only reads.");
|
|
55
|
-
}
|
|
36
|
+
requireTargetFor(action, pullNumber, runId);
|
|
37
|
+
refuseFlagsFromOtherActions(values, action);
|
|
56
38
|
const title = optionalNonEmpty(values.flags.get("--title"));
|
|
57
39
|
if (action === "retitle" && !title)
|
|
58
40
|
throw new Error("github retitle needs --title \"<new title>\".");
|
|
@@ -78,4 +60,28 @@ export function parseGithubArgs(args) {
|
|
|
78
60
|
dashboardUrl: optionalUrl(values.flags.get("--dashboard-url")),
|
|
79
61
|
json: values.booleans.has("--json"),
|
|
80
62
|
};
|
|
63
|
+
}
|
|
64
|
+
function requireTargetFor(action, pullNumber, runId) {
|
|
65
|
+
if (action === "rerun") {
|
|
66
|
+
if ((pullNumber === undefined) === (runId === undefined)) {
|
|
67
|
+
throw new Error("github rerun needs exactly one of a pull request number or --run <id>.");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (pullNumber === undefined) {
|
|
71
|
+
throw new Error(`github ${action} needs a pull request number.`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function refuseFlagsFromOtherActions(values, action) {
|
|
75
|
+
const only = (flag, allowed) => {
|
|
76
|
+
if (values.flags.has(flag) && !allowed.includes(action)) {
|
|
77
|
+
throw new Error(`${flag} belongs to \`cockpit github ${allowed.join("|")}\`.`);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
only("--claim", ["pr"]);
|
|
81
|
+
only("--method", ["merge"]);
|
|
82
|
+
only("--title", ["retitle"]);
|
|
83
|
+
only("--run", ["rerun"]);
|
|
84
|
+
if (values.flags.has("--confirm") && action === "pr") {
|
|
85
|
+
throw new Error("--confirm belongs to merge, retitle and rerun; pr only reads.");
|
|
86
|
+
}
|
|
81
87
|
}
|
|
@@ -37,27 +37,39 @@ export function parseHitsArgs(args) {
|
|
|
37
37
|
}
|
|
38
38
|
if (given("--weeks") && action !== "timeline")
|
|
39
39
|
throw new Error("--weeks belongs to hits timeline.");
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
throw new Error("--scope and --version belong to hits approve.");
|
|
51
|
-
return { ...common, action };
|
|
52
|
-
}
|
|
53
|
-
if (action === "approve") {
|
|
54
|
-
const scope = optionalNonEmpty(values.flags.get("--scope"));
|
|
55
|
-
if (!scope || !SCOPE.test(scope))
|
|
56
|
-
throw new Error("hits approve wants --scope team, agents or project:<id> (for example project:pearl-portal).");
|
|
57
|
-
return { ...common, action, scope, ...versionOf(values.flags.get("--version")) };
|
|
58
|
-
}
|
|
40
|
+
const parsed = { values, given, common };
|
|
41
|
+
if (action === "timeline")
|
|
42
|
+
return parseHitsTimeline(parsed);
|
|
43
|
+
if (action === "coach")
|
|
44
|
+
return parseHitsCoach(parsed);
|
|
45
|
+
if (action === "approve")
|
|
46
|
+
return parseHitsApprove(parsed);
|
|
47
|
+
return parseHitsRead(parsed);
|
|
48
|
+
}
|
|
49
|
+
function refuseApproveFlags({ given }) {
|
|
59
50
|
if (given("--scope") || given("--version"))
|
|
60
51
|
throw new Error("--scope and --version belong to hits approve.");
|
|
52
|
+
}
|
|
53
|
+
function parseHitsTimeline(parsed) {
|
|
54
|
+
refuseApproveFlags(parsed);
|
|
55
|
+
const weeks = optionalPositiveInteger(parsed.values.flags.get("--weeks"), "--weeks");
|
|
56
|
+
if (weeks !== undefined && weeks > 26)
|
|
57
|
+
throw new Error("--weeks must be 26 or fewer.");
|
|
58
|
+
return { ...parsed.common, action: "timeline", ...(weeks !== undefined ? { weeks } : {}) };
|
|
59
|
+
}
|
|
60
|
+
function parseHitsCoach(parsed) {
|
|
61
|
+
refuseApproveFlags(parsed);
|
|
62
|
+
return { ...parsed.common, action: "coach" };
|
|
63
|
+
}
|
|
64
|
+
function parseHitsApprove({ values, common }) {
|
|
65
|
+
const scope = optionalNonEmpty(values.flags.get("--scope"));
|
|
66
|
+
if (!scope || !SCOPE.test(scope))
|
|
67
|
+
throw new Error("hits approve wants --scope team, agents or project:<id> (for example project:pearl-portal).");
|
|
68
|
+
return { ...common, action: "approve", scope, ...versionOf(values.flags.get("--version")) };
|
|
69
|
+
}
|
|
70
|
+
function parseHitsRead(parsed) {
|
|
71
|
+
const { values, given, common } = parsed;
|
|
72
|
+
refuseApproveFlags(parsed);
|
|
61
73
|
const whoFlags = ["--mine", "--person", "--agent", "--project"].filter(given);
|
|
62
74
|
if (whoFlags.length > 1)
|
|
63
75
|
throw new Error(`Pick one of --mine, --person, --agent or --project (got ${whoFlags.join(" and ")}).`);
|
|
@@ -77,7 +89,7 @@ export function parseHitsArgs(args) {
|
|
|
77
89
|
throw new Error("--learning is read for one person: use --mine or --person <name>.");
|
|
78
90
|
return {
|
|
79
91
|
...common,
|
|
80
|
-
action,
|
|
92
|
+
action: "read",
|
|
81
93
|
...(who ? { who } : {}),
|
|
82
94
|
...(person ? { person } : {}),
|
|
83
95
|
...(project ? { project } : {}),
|