@bli-cockpit/cli 0.2.242 → 0.2.244
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/git-state.js +1 -0
- package/dist/adapters/raw-evidence-budget-admission.js +63 -0
- package/dist/adapters/raw-evidence-collection-state.js +6 -50
- package/dist/adapters/raw-evidence-git-diff.js +1 -0
- package/dist/agent-rules.js +8 -0
- package/dist/autostart-contract.js +12 -0
- package/dist/autostart-darwin-plist.js +2 -2
- package/dist/autostart-darwin.js +1 -1
- package/dist/autostart-remind.js +245 -0
- package/dist/autostart-self-heal.js +17 -1
- package/dist/autostart-windows-scripts.js +7 -7
- package/dist/autostart.js +16 -15
- package/dist/commands/approvals-check-where.js +14 -0
- package/dist/commands/approvals-render.js +8 -1
- package/dist/commands/approvals.js +3 -2
- package/dist/commands/autostart-command.js +10 -0
- package/dist/commands/autostart-heal.js +36 -1
- package/dist/commands/brain.js +1 -1
- package/dist/commands/cal-agenda.js +44 -0
- package/dist/commands/cal-availability-share.js +97 -0
- package/dist/commands/cal-booking.js +147 -0
- package/dist/commands/cal-join.js +62 -0
- package/dist/commands/cal-links.js +68 -0
- package/dist/commands/cal-people.js +136 -0
- package/dist/commands/cal-remind.js +176 -0
- package/dist/commands/cal-reminders.js +33 -0
- package/dist/commands/cal-suggest.js +89 -0
- package/dist/commands/cal.js +41 -1
- package/dist/commands/cycle-args.js +127 -0
- package/dist/commands/cycles.js +117 -0
- package/dist/commands/docs-comments-args.js +85 -0
- package/dist/commands/docs-comments.js +128 -0
- package/dist/commands/docs-files-args.js +49 -0
- package/dist/commands/docs-files.js +179 -0
- package/dist/commands/docs-formatting.js +45 -0
- package/dist/commands/docs-inbox-args.js +93 -0
- package/dist/commands/docs-inbox.js +126 -0
- package/dist/commands/docs-notion.js +5 -0
- package/dist/commands/docs-read.js +2 -1
- package/dist/commands/docs-recovery.js +173 -0
- package/dist/commands/docs-refs-args.js +104 -0
- package/dist/commands/docs-refs.js +153 -0
- package/dist/commands/docs-search-args.js +79 -0
- package/dist/commands/docs-search.js +64 -0
- package/dist/commands/docs-sync-args.js +82 -0
- package/dist/commands/docs-sync.js +197 -0
- package/dist/commands/docs-tree.js +136 -0
- package/dist/commands/docs.js +85 -51
- package/dist/commands/doctor-update.js +4 -1
- package/dist/commands/inbox-args.js +81 -0
- package/dist/commands/inbox.js +88 -0
- package/dist/commands/issue-batch.js +61 -0
- package/dist/commands/issue-relations.js +151 -0
- package/dist/commands/issue-write.js +38 -5
- package/dist/commands/issue.js +32 -1
- package/dist/commands/local-args-collector-sources.js +2 -1
- package/dist/commands/local-args-tower-approvals.js +16 -4
- package/dist/commands/local-args-tower-cal-booking.js +81 -0
- package/dist/commands/local-args-tower-cal-links.js +42 -0
- package/dist/commands/local-args-tower-cal-remind.js +74 -0
- package/dist/commands/local-args-tower-cal-share.js +41 -0
- package/dist/commands/local-args-tower-cal.js +81 -8
- package/dist/commands/local-args-tower-docs-msg.js +98 -9
- package/dist/commands/local-args-tower-msg.js +222 -10
- package/dist/commands/local-args-tower-search.js +32 -5
- package/dist/commands/local-args-tower-work.js +110 -34
- package/dist/commands/local-auth.js +1 -1
- package/dist/commands/local-help-commands-tower-ops.js +6 -4
- package/dist/commands/local-help-commands-tower.js +47 -31
- package/dist/commands/local-help-commands.js +3 -1
- package/dist/commands/local-help-cycles.js +9 -0
- package/dist/commands/local-help-docs-files.js +10 -0
- package/dist/commands/local-help-docs-formatting.js +7 -0
- package/dist/commands/local-help-docs-inbox.js +10 -0
- package/dist/commands/local-help-docs-recovery.js +10 -0
- package/dist/commands/local-help-docs-refs.js +11 -0
- package/dist/commands/local-help-docs-sync.js +9 -0
- package/dist/commands/local-help-inbox.js +9 -0
- package/dist/commands/local-help-project.js +21 -0
- package/dist/commands/local-help-templates.js +9 -0
- package/dist/commands/local-help-views.js +10 -0
- package/dist/commands/local-help-work-notify.js +6 -0
- package/dist/commands/local-help.js +3 -3
- package/dist/commands/manager-hints.js +4 -2
- package/dist/commands/manager-limits.json +3 -1
- package/dist/commands/manager-read-reasons.js +1 -0
- package/dist/commands/manager-render-home.js +13 -5
- package/dist/commands/manager-render.js +3 -0
- package/dist/commands/manager.js +3 -1
- package/dist/commands/mcp-stdio-probe.js +1 -0
- package/dist/commands/msg-activity.js +134 -0
- package/dist/commands/msg-channel-args.js +92 -0
- package/dist/commands/msg-channel.js +125 -0
- package/dist/commands/msg-context-args.js +67 -0
- package/dist/commands/msg-context.js +65 -0
- package/dist/commands/msg-dm.js +28 -0
- package/dist/commands/msg-file.js +185 -0
- package/dist/commands/msg-mark-args.js +78 -0
- package/dist/commands/msg-mark.js +148 -0
- package/dist/commands/msg-notify-args.js +100 -0
- package/dist/commands/msg-notify.js +122 -0
- package/dist/commands/msg-schedule-args.js +114 -0
- package/dist/commands/msg-schedule.js +226 -0
- package/dist/commands/msg-search-args.js +71 -0
- package/dist/commands/msg-search.js +48 -0
- package/dist/commands/msg-write.js +15 -1
- package/dist/commands/msg.js +62 -39
- package/dist/commands/project-args.js +82 -0
- package/dist/commands/project.js +71 -7
- package/dist/commands/public-root.js +1 -1
- package/dist/commands/search.js +23 -0
- package/dist/commands/session-sync-failures.js +17 -4
- package/dist/commands/session-sync-health.js +7 -2
- package/dist/commands/session-sync-hold.js +42 -1
- package/dist/commands/session-sync-outside-roots.js +72 -14
- package/dist/commands/session-sync-upload.js +2 -0
- package/dist/commands/session-sync.js +1 -0
- package/dist/commands/sources-check.js +63 -2
- package/dist/commands/sources-discovery.js +1 -1
- package/dist/commands/sources-reconciliation.js +87 -0
- package/dist/commands/sync-doctor.js +14 -1
- package/dist/commands/sync-report.js +1 -1
- package/dist/commands/template-args.js +122 -0
- package/dist/commands/templates.js +162 -0
- package/dist/commands/view-args.js +131 -0
- package/dist/commands/views.js +138 -0
- package/dist/commands/work-notify-args.js +67 -0
- package/dist/commands/work-notify.js +63 -0
- package/dist/delivery-hold-notice.js +34 -0
- package/dist/evidence-transcripts-client.js +1 -0
- package/dist/log-rotation.js +2 -2
- package/dist/notifier.js +83 -0
- package/dist/process-runner.js +4 -0
- package/dist/repo-identity-git.js +1 -0
- package/package.json +4 -4
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
* behaves the way it looks, without demanding quotes.
|
|
15
15
|
*/
|
|
16
16
|
import { optionalNonEmpty, optionalPositiveInteger, optionalUrl, parseNamedArgs, } from "./local-arg-values.js";
|
|
17
|
+
import { DOC_SEARCH_BOOLEAN_FLAGS, DOC_SEARCH_VALUE_FLAGS, hasDocSearchFlags, readDocSearchFilters } from "./docs-search-args.js";
|
|
18
|
+
import { hasMsgSearchFilterFlags, MSG_SEARCH_BOOLEAN_FLAGS, MSG_SEARCH_VALUE_FLAGS, readMsgSearchFilters } from "./msg-search-args.js";
|
|
17
19
|
/**
|
|
18
20
|
* What `--kind` names is the SERVER's to say (BLI-13027). This used to be a copy
|
|
19
21
|
* of the dashboard's kind list that refused anything it did not hold, so an
|
|
@@ -23,14 +25,11 @@ import { optionalNonEmpty, optionalPositiveInteger, optionalUrl, parseNamedArgs,
|
|
|
23
25
|
*/
|
|
24
26
|
export function parseSearchArgs(args) {
|
|
25
27
|
const values = parseNamedArgs(args, {
|
|
26
|
-
allowedFlags: ["--home", "--dashboard-url", "--kind", "--limit", "--json", "--categories"],
|
|
27
|
-
valueFlags: ["--home", "--dashboard-url", "--kind", "--limit"],
|
|
28
|
+
allowedFlags: ["--home", "--dashboard-url", "--kind", "--limit", "--json", "--categories", "--from", "--timezone", ...MSG_SEARCH_VALUE_FLAGS, ...MSG_SEARCH_BOOLEAN_FLAGS, ...DOC_SEARCH_VALUE_FLAGS, ...DOC_SEARCH_BOOLEAN_FLAGS],
|
|
29
|
+
valueFlags: ["--home", "--dashboard-url", "--kind", "--limit", "--from", "--timezone", ...MSG_SEARCH_VALUE_FLAGS, ...DOC_SEARCH_VALUE_FLAGS],
|
|
28
30
|
});
|
|
29
31
|
const query = values.positionals.join(" ").trim();
|
|
30
32
|
const categories = values.booleans.has("--categories");
|
|
31
|
-
if (query.length === 0 && !categories) {
|
|
32
|
-
throw new Error('search needs something to search for, e.g. `cockpit search "storage ceiling"`.');
|
|
33
|
-
}
|
|
34
33
|
const kindFlag = optionalNonEmpty(values.flags.get("--kind"));
|
|
35
34
|
let kinds;
|
|
36
35
|
if (kindFlag) {
|
|
@@ -42,11 +41,39 @@ export function parseSearchArgs(args) {
|
|
|
42
41
|
if (kinds.length === 0)
|
|
43
42
|
kinds = undefined;
|
|
44
43
|
}
|
|
44
|
+
const only = (kind) => Boolean(kinds && kinds.length === 1 && kinds[0] === kind);
|
|
45
|
+
// BLI-21697: with --kind doc, --by --under --since --until --timezone --sort --title-only --word filter documents.
|
|
46
|
+
const docFilters = readDocSearchFilters(values);
|
|
47
|
+
const docFiltered = hasDocSearchFlags(docFilters);
|
|
48
|
+
// BLI-21658: message filters, the same flags as `cockpit msg search`; only meaningful with --kind msg.
|
|
49
|
+
const msgFilters = readMsgSearchFilters(values);
|
|
50
|
+
const filtered = !only("doc") && hasMsgSearchFilterFlags(msgFilters);
|
|
51
|
+
const usedZone = values.flags.has("--timezone");
|
|
52
|
+
if (query.length === 0 && !categories && !filtered && !(only("doc") && docFiltered)) {
|
|
53
|
+
throw new Error('search needs something to search for, e.g. `cockpit search "storage ceiling"`.');
|
|
54
|
+
}
|
|
55
|
+
if (only("doc")) {
|
|
56
|
+
// Message-only flags have no meaning for documents.
|
|
57
|
+
if (values.flags.has("--in") || values.flags.has("--from") || values.booleans.has("--replies") || values.booleans.has("--top-level") || values.booleans.has("--has-file")) {
|
|
58
|
+
throw new Error("--in, --from, --replies, --top-level and --has-file filter messages; with --kind doc use --by, --under, --since, --until, --timezone, --sort, --title-only and --word.");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const docOnly = ["--by", "--under", "--sort"].some((flag) => values.flags.has(flag)) || values.booleans.has("--title-only") || values.booleans.has("--word");
|
|
63
|
+
if (docOnly) {
|
|
64
|
+
throw new Error("--by, --under, --sort, --title-only and --word filter documents and need --kind doc, e.g. `cockpit search runbook --kind doc --by me`.");
|
|
65
|
+
}
|
|
66
|
+
if ((filtered || usedZone) && !only("msg")) {
|
|
67
|
+
throw new Error("--from, --in, --since, --until, --timezone, --replies, --top-level and --has-file filter messages and need --kind msg, e.g. `cockpit search deploy --kind msg --from me`. (Documents take --by, --under and --since with --kind doc.)");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
45
70
|
return {
|
|
46
71
|
kind: "search",
|
|
47
72
|
query,
|
|
48
73
|
...(categories ? { categories: true } : {}),
|
|
49
74
|
...(kinds ? { kinds } : {}),
|
|
75
|
+
...(filtered ? { msgFilters } : {}),
|
|
76
|
+
...(only("doc") && (docFiltered || usedZone) ? { docFilters } : {}),
|
|
50
77
|
limit: optionalPositiveInteger(values.flags.get("--limit"), "--limit"),
|
|
51
78
|
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
52
79
|
dashboardUrl: optionalUrl(values.flags.get("--dashboard-url")),
|
|
@@ -16,7 +16,13 @@
|
|
|
16
16
|
* shared machine and lands in shell history).
|
|
17
17
|
*/
|
|
18
18
|
import { optionalNonEmpty, optionalPositiveInteger, optionalUrl, parseNamedArgs, } from "./local-arg-values.js";
|
|
19
|
+
import { RELATION_VALUE_FLAGS, readRelationFlags } from "./issue-relations.js";
|
|
19
20
|
import { estimateForSize } from "./issue-contracts.js";
|
|
21
|
+
import { INBOX_ACTIONS, INBOX_BOOLEAN_FLAGS, INBOX_VALUE_FLAGS, isInboxAction, parseInboxArgs } from "./inbox-args.js";
|
|
22
|
+
import { NOTIFY_ACTIONS, NOTIFY_VALUE_FLAGS, isNotifyAction, parseNotifyArgs } from "./work-notify-args.js";
|
|
23
|
+
import { VIEW_BOOLEAN_FLAGS, VIEW_VALUE_FLAGS, parseViewArgs } from "./view-args.js";
|
|
24
|
+
import { parseTemplateArgs } from "./template-args.js";
|
|
25
|
+
import { CYCLE_BOOLEAN_FLAGS, CYCLE_VALUE_FLAGS, parseCycleArgs, readCycleField } from "./cycle-args.js";
|
|
20
26
|
const ISSUE_ACTIONS = new Set([
|
|
21
27
|
"mutation-status",
|
|
22
28
|
"board",
|
|
@@ -29,6 +35,15 @@ const ISSUE_ACTIONS = new Set([
|
|
|
29
35
|
"history",
|
|
30
36
|
"versions",
|
|
31
37
|
"restore",
|
|
38
|
+
"label",
|
|
39
|
+
"batch",
|
|
40
|
+
"relations",
|
|
41
|
+
"relate",
|
|
42
|
+
"unrelate",
|
|
43
|
+
"views",
|
|
44
|
+
"templates", "cycles",
|
|
45
|
+
...INBOX_ACTIONS,
|
|
46
|
+
...NOTIFY_ACTIONS,
|
|
32
47
|
]);
|
|
33
48
|
/** Every verb except `list` and `create` names ONE issue first. */
|
|
34
49
|
const ISSUE_ACTIONS_NEEDING_AN_ISSUE = new Set([
|
|
@@ -40,6 +55,10 @@ const ISSUE_ACTIONS_NEEDING_AN_ISSUE = new Set([
|
|
|
40
55
|
"history",
|
|
41
56
|
"versions",
|
|
42
57
|
"restore",
|
|
58
|
+
"label",
|
|
59
|
+
"relations",
|
|
60
|
+
"relate",
|
|
61
|
+
"unrelate",
|
|
43
62
|
]);
|
|
44
63
|
/**
|
|
45
64
|
* The closed state vocabulary, copied verbatim from
|
|
@@ -66,6 +85,9 @@ const ISSUE_ALLOWED_FLAGS = [
|
|
|
66
85
|
"--priority",
|
|
67
86
|
"--size",
|
|
68
87
|
"--parent",
|
|
88
|
+
"--due",
|
|
89
|
+
"--add",
|
|
90
|
+
"--remove",
|
|
69
91
|
"--file",
|
|
70
92
|
"--version",
|
|
71
93
|
"--cursor",
|
|
@@ -79,8 +101,15 @@ const ISSUE_ALLOWED_FLAGS = [
|
|
|
79
101
|
"--all",
|
|
80
102
|
"--body-stdin",
|
|
81
103
|
"--json",
|
|
104
|
+
...VIEW_BOOLEAN_FLAGS,
|
|
105
|
+
...VIEW_VALUE_FLAGS,
|
|
106
|
+
...INBOX_BOOLEAN_FLAGS,
|
|
107
|
+
...INBOX_VALUE_FLAGS,
|
|
108
|
+
...NOTIFY_VALUE_FLAGS,
|
|
109
|
+
...RELATION_VALUE_FLAGS, ...CYCLE_VALUE_FLAGS, ...CYCLE_BOOLEAN_FLAGS,
|
|
82
110
|
];
|
|
83
111
|
const ISSUE_VALUE_FLAGS = [
|
|
112
|
+
"--cycle",
|
|
84
113
|
"--request-id",
|
|
85
114
|
"--expected-revision",
|
|
86
115
|
"--home",
|
|
@@ -95,6 +124,9 @@ const ISSUE_VALUE_FLAGS = [
|
|
|
95
124
|
"--priority",
|
|
96
125
|
"--size",
|
|
97
126
|
"--parent",
|
|
127
|
+
"--due",
|
|
128
|
+
"--add",
|
|
129
|
+
"--remove",
|
|
98
130
|
"--file",
|
|
99
131
|
"--version",
|
|
100
132
|
"--cursor",
|
|
@@ -105,6 +137,10 @@ const ISSUE_VALUE_FLAGS = [
|
|
|
105
137
|
"--updated-before",
|
|
106
138
|
"--sort",
|
|
107
139
|
"--dir",
|
|
140
|
+
...VIEW_VALUE_FLAGS,
|
|
141
|
+
...INBOX_VALUE_FLAGS,
|
|
142
|
+
...NOTIFY_VALUE_FLAGS,
|
|
143
|
+
...RELATION_VALUE_FLAGS, ...CYCLE_VALUE_FLAGS,
|
|
108
144
|
];
|
|
109
145
|
export function parseIssueArgs(args) {
|
|
110
146
|
const values = parseNamedArgs(args, {
|
|
@@ -112,6 +148,25 @@ export function parseIssueArgs(args) {
|
|
|
112
148
|
valueFlags: ISSUE_VALUE_FLAGS,
|
|
113
149
|
});
|
|
114
150
|
const { action, rest } = readIssueAction(values);
|
|
151
|
+
if (action === "views")
|
|
152
|
+
return parseViewArgs(values, rest);
|
|
153
|
+
if (action === "templates")
|
|
154
|
+
return parseTemplateArgs(values, rest);
|
|
155
|
+
if (action === "cycles")
|
|
156
|
+
return parseCycleArgs(values, rest);
|
|
157
|
+
if (isNotifyAction(action))
|
|
158
|
+
return parseNotifyArgs(values, rest);
|
|
159
|
+
if (isInboxAction(action))
|
|
160
|
+
return parseInboxArgs(values, action, rest);
|
|
161
|
+
const strayNotify = NOTIFY_VALUE_FLAGS.find((flag) => values.flags.has(flag));
|
|
162
|
+
if (strayNotify)
|
|
163
|
+
throw new Error(`${strayNotify} belongs to \`cockpit ticket notifications\`.`);
|
|
164
|
+
const strayInbox = [...INBOX_VALUE_FLAGS, ...INBOX_BOOLEAN_FLAGS].find((flag) => values.flags.has(flag) || values.booleans.has(flag));
|
|
165
|
+
if (strayInbox)
|
|
166
|
+
throw new Error(`${strayInbox} belongs to \`cockpit ticket inbox\`.`);
|
|
167
|
+
const stray = [...VIEW_VALUE_FLAGS, ...VIEW_BOOLEAN_FLAGS].find((flag) => values.flags.has(flag) || values.booleans.has(flag));
|
|
168
|
+
if (stray)
|
|
169
|
+
throw new Error(`${stray} belongs to \`cockpit ticket views\`.`);
|
|
115
170
|
const { issueRef, issueRefs, moveState } = readIssueReference(action, rest);
|
|
116
171
|
const requestId = optionalNonEmpty(values.flags.get("--request-id"));
|
|
117
172
|
const expectedRevision = optionalPositiveInteger(values.flags.get("--expected-revision"), "--expected-revision");
|
|
@@ -119,11 +174,12 @@ export function parseIssueArgs(args) {
|
|
|
119
174
|
throw new Error("--request-id must be a UUID.");
|
|
120
175
|
if (action === "mutation-status" && !UUID_PATTERN.test(issueRef ?? ""))
|
|
121
176
|
throw new Error("mutation-status needs a request UUID.");
|
|
122
|
-
if ((requestId || expectedRevision !== undefined) && !["create", "update", "move", "comment", "restore"].includes(action)) {
|
|
123
|
-
throw new Error("Write-safety flags belong to create, update, move, comment or
|
|
177
|
+
if ((requestId || expectedRevision !== undefined) && !["create", "update", "move", "comment", "restore", "label", "batch", "relate", "unrelate"].includes(action)) {
|
|
178
|
+
throw new Error("Write-safety flags belong to create, update, move, comment, restore, label, relate, unrelate or batch.");
|
|
124
179
|
}
|
|
125
180
|
validateIssueState(moveState, "issue move state must be one of: ");
|
|
126
181
|
const stateFilter = readIssueStateFilter(action, values);
|
|
182
|
+
const fields = readIssueFields(action, values);
|
|
127
183
|
const title = readIssueTitle(action, values);
|
|
128
184
|
if (action !== "board" && values.flags.has("--rows")) {
|
|
129
185
|
throw new Error("--rows widens cockpit tickets board only.");
|
|
@@ -140,7 +196,7 @@ export function parseIssueArgs(args) {
|
|
|
140
196
|
const assignee = readIssueAssignee(action, values);
|
|
141
197
|
const limit = readIssueLimit(action, values);
|
|
142
198
|
const versionNumber = readIssueVersion(action, values);
|
|
143
|
-
return buildIssueCommand(values, action, issueRef, issueRefs, moveState, stateFilter, title, priority, estimate, assignee, limit, versionNumber, page);
|
|
199
|
+
return { ...buildIssueCommand(values, action, issueRef, issueRefs, moveState, stateFilter, title, priority, estimate, assignee, limit, versionNumber, page, fields), ...readRelationFlags(action, values) };
|
|
144
200
|
}
|
|
145
201
|
const INSTANT = /^\d{4}-\d\d-\d\d(?:T\d\d:\d\d:\d\d(?:\.\d{1,6})?(?:Z|[+-]\d\d:\d\d))?$/;
|
|
146
202
|
/** BLI-21315: the cursor contract's flags. Filters and sort belong to `list`; `--cursor` also to `history`. */
|
|
@@ -190,7 +246,7 @@ function readIssueAction(values) {
|
|
|
190
246
|
const first = values.positionals[0];
|
|
191
247
|
const action = (first === undefined ? "list" : first);
|
|
192
248
|
if (!ISSUE_ACTIONS.has(action)) {
|
|
193
|
-
throw new Error(`Unknown issue command: ${first}. Try board, list, show, create, update, move, comment, history, versions, restore, or mutation-status.`);
|
|
249
|
+
throw new Error(`Unknown issue command: ${first}. Try board, list, show, create, update, move, comment, label, relations, relate, unrelate, views, templates, cycles, inbox, read, unread, subscribe, unsubscribe, notifications, history, versions, restore, batch, or mutation-status.`);
|
|
194
250
|
}
|
|
195
251
|
return { action, rest: values.positionals.slice(first === undefined ? 0 : 1) };
|
|
196
252
|
}
|
|
@@ -237,10 +293,11 @@ function validateIssueState(state, errorPrefix) {
|
|
|
237
293
|
function readIssueStateFilter(action, values) {
|
|
238
294
|
const stateFilter = optionalNonEmpty(values.flags.get("--state"));
|
|
239
295
|
validateIssueState(stateFilter, "--state must be one of: ");
|
|
240
|
-
if (stateFilter !== undefined && action !== "list" && action !== "board") {
|
|
241
|
-
throw new Error("--state filters `cockpit issue list`; to move an issue use `cockpit issue move <id> <state>`.");
|
|
296
|
+
if (stateFilter !== undefined && action !== "list" && action !== "board" && action !== "create") {
|
|
297
|
+
throw new Error("--state filters `cockpit issue list` and sets the starting state on `create`; to move an issue use `cockpit issue move <id> <state>`.");
|
|
242
298
|
}
|
|
243
|
-
|
|
299
|
+
// On create the same flag is the starting state; it is returned separately in readIssueFields.
|
|
300
|
+
return action === "create" ? undefined : stateFilter;
|
|
244
301
|
}
|
|
245
302
|
function readIssueTitle(action, values) {
|
|
246
303
|
const title = optionalNonEmpty(values.flags.get("--title"));
|
|
@@ -270,14 +327,54 @@ function readIssueAssignee(action, values) {
|
|
|
270
327
|
// email is refused HERE rather than travelling to Postgres as a malformed
|
|
271
328
|
// uuid, which would come back as a 500 that names nothing useful.
|
|
272
329
|
const assignee = optionalNonEmpty(values.flags.get("--assignee"));
|
|
273
|
-
if (assignee !== undefined && assignee !== "me" && assignee !== "unassigned" && !UUID_PATTERN.test(assignee)) {
|
|
274
|
-
throw new Error('--assignee must be "me", "unassigned", or a person\'s uuid (see `cockpit team`).');
|
|
330
|
+
if (assignee !== undefined && assignee !== "me" && assignee !== "unassigned" && assignee !== "none" && !UUID_PATTERN.test(assignee)) {
|
|
331
|
+
throw new Error('--assignee must be "me", "unassigned" (list), "none" (update), or a person\'s uuid (see `cockpit team`).');
|
|
275
332
|
}
|
|
276
333
|
if (assignee === "unassigned" && action !== "list" && action !== "board") {
|
|
277
|
-
throw new Error(
|
|
334
|
+
throw new Error("--assignee unassigned filters `cockpit ticket list`; to clear an owner on update pass --assignee none.");
|
|
278
335
|
}
|
|
336
|
+
if (assignee === "none" && action !== "update")
|
|
337
|
+
throw new Error("--assignee none clears the owner on `cockpit ticket update`.");
|
|
279
338
|
return assignee;
|
|
280
339
|
}
|
|
340
|
+
const CALENDAR_DAY = /^\d{4}-\d{2}-\d{2}$/;
|
|
341
|
+
/** BLI-21422: starting state, due date and label names. Omitted stays omitted; `none` is the only way to clear. */
|
|
342
|
+
function readIssueFields(action, values) {
|
|
343
|
+
const out = {};
|
|
344
|
+
const startState = optionalNonEmpty(values.flags.get("--state"));
|
|
345
|
+
if (action === "create" && startState)
|
|
346
|
+
out.startState = startState;
|
|
347
|
+
const due = optionalNonEmpty(values.flags.get("--due"));
|
|
348
|
+
if (due !== undefined) {
|
|
349
|
+
if (action !== "create" && action !== "update")
|
|
350
|
+
throw new Error("--due belongs to `cockpit ticket create` and `update`.");
|
|
351
|
+
if (due === "none") {
|
|
352
|
+
if (action === "create")
|
|
353
|
+
throw new Error("--due none clears a date on `cockpit ticket update`; leave --due off to create without one.");
|
|
354
|
+
out.due = null;
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
if (!CALENDAR_DAY.test(due) || new Date(`${due}T12:00:00Z`).toISOString().slice(0, 10) !== due) {
|
|
358
|
+
throw new Error("--due must be a calendar date as YYYY-MM-DD, or none to clear it (update).");
|
|
359
|
+
}
|
|
360
|
+
out.due = due;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
const add = (values.flagValues.get("--add") ?? []).map((v) => v.trim()).filter(Boolean);
|
|
364
|
+
const remove = (values.flagValues.get("--remove") ?? []).map((v) => v.trim()).filter(Boolean);
|
|
365
|
+
if ((add.length > 0 || remove.length > 0) && action !== "label") {
|
|
366
|
+
throw new Error("--add and --remove belong to `cockpit ticket label <id>`.");
|
|
367
|
+
}
|
|
368
|
+
if (action === "label") {
|
|
369
|
+
if (add.length === 0 && remove.length === 0)
|
|
370
|
+
throw new Error("ticket label needs --add <name> and/or --remove <name> (repeat the flag for several).");
|
|
371
|
+
if (add.length > 50 || remove.length > 50)
|
|
372
|
+
throw new Error("ticket label changes at most 50 labels per side.");
|
|
373
|
+
out.addLabels = add;
|
|
374
|
+
out.removeLabels = remove;
|
|
375
|
+
}
|
|
376
|
+
return out;
|
|
377
|
+
}
|
|
281
378
|
/** `restore` names the version it wants; there is no "restore the previous one" (BLI-21300). */
|
|
282
379
|
function readIssueVersion(action, values) {
|
|
283
380
|
const version = optionalPositiveInteger(values.flags.get("--version"), "--version");
|
|
@@ -295,7 +392,7 @@ function readIssueLimit(action, values) {
|
|
|
295
392
|
}
|
|
296
393
|
return limit;
|
|
297
394
|
}
|
|
298
|
-
function buildIssueCommand(values, action, issueRef, issueRefs, moveState, stateFilter, title, priority, estimate, assignee, limit, versionNumber, page) {
|
|
395
|
+
function buildIssueCommand(values, action, issueRef, issueRefs, moveState, stateFilter, title, priority, estimate, assignee, limit, versionNumber, page, fields) {
|
|
299
396
|
return {
|
|
300
397
|
kind: "issue",
|
|
301
398
|
requestId: optionalNonEmpty(values.flags.get("--request-id")),
|
|
@@ -313,6 +410,7 @@ function buildIssueCommand(values, action, issueRef, issueRefs, moveState, state
|
|
|
313
410
|
project: optionalNonEmpty(values.flags.get("--project")),
|
|
314
411
|
...(limit === undefined ? {} : { limit }),
|
|
315
412
|
...page,
|
|
413
|
+
...fields, ...readCycleField(action, values),
|
|
316
414
|
...(versionNumber === undefined ? {} : { versionNumber }),
|
|
317
415
|
title,
|
|
318
416
|
...(priority === undefined ? {} : { priority }),
|
|
@@ -323,26 +421,4 @@ function buildIssueCommand(values, action, issueRef, issueRefs, moveState, state
|
|
|
323
421
|
json: values.booleans.has("--json"),
|
|
324
422
|
};
|
|
325
423
|
}
|
|
326
|
-
export
|
|
327
|
-
const values = parseNamedArgs(args, {
|
|
328
|
-
allowedFlags: ["--home", "--dashboard-url", "--archived", "--cursor", "--limit", "--json"],
|
|
329
|
-
valueFlags: ["--home", "--dashboard-url", "--cursor", "--limit"],
|
|
330
|
-
});
|
|
331
|
-
const first = values.positionals[0];
|
|
332
|
-
const action = (first === undefined ? "list" : first);
|
|
333
|
-
if (action !== "list")
|
|
334
|
-
throw new Error(`Unknown project command: ${first}. The only verb is list.`);
|
|
335
|
-
const rest = values.positionals.slice(first === undefined ? 0 : 1);
|
|
336
|
-
if (rest.length > 0)
|
|
337
|
-
throw new Error(`project list does not take "${rest[0]}".`);
|
|
338
|
-
return {
|
|
339
|
-
kind: "project",
|
|
340
|
-
action,
|
|
341
|
-
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
342
|
-
dashboardUrl: optionalUrl(values.flags.get("--dashboard-url")),
|
|
343
|
-
includeArchived: values.booleans.has("--archived"),
|
|
344
|
-
...(optionalNonEmpty(values.flags.get("--cursor")) ? { cursor: optionalNonEmpty(values.flags.get("--cursor")) } : {}),
|
|
345
|
-
...(values.flags.has("--limit") ? { limit: optionalPositiveInteger(values.flags.get("--limit"), "--limit") } : {}),
|
|
346
|
-
json: values.booleans.has("--json"),
|
|
347
|
-
};
|
|
348
|
-
}
|
|
424
|
+
export { parseProjectArgs } from "./project-args.js";
|
|
@@ -235,7 +235,7 @@ async function inferUniqueGitEmail(roots) {
|
|
|
235
235
|
}
|
|
236
236
|
async function readGitConfigEmail(root) {
|
|
237
237
|
return new Promise((resolve) => {
|
|
238
|
-
execFile("git", ["config", "user.email"], { cwd: root, encoding: "utf8" }, (error, stdout) => {
|
|
238
|
+
execFile("git", ["config", "user.email"], { cwd: root, encoding: "utf8", windowsHide: true }, (error, stdout) => {
|
|
239
239
|
if (error) {
|
|
240
240
|
resolve(null);
|
|
241
241
|
return;
|
|
@@ -174,6 +174,8 @@ export const TOWER_OPS_COMMAND_HELP = [
|
|
|
174
174
|
" cockpit approvals show <id> the card in full: who, why, the exact content, every receipt",
|
|
175
175
|
" cockpit approvals create prepare a card; the POST /api/approvals body as JSON on stdin",
|
|
176
176
|
" cockpit approvals approve <id> [--edit] let the guarded act run; --edit reads {\"subject\":...,\"text\":...} or {\"content\":...} on stdin",
|
|
177
|
+
" --checked-sent sent|not-sent on a card whose delivery is unknown or stuck sending, what you found where it was going",
|
|
178
|
+
" (your Sent folder for mail.send, the channel for msg.send): sent closes it, not-sent sends or posts it now",
|
|
177
179
|
" cockpit approvals edit <id> [--approve] save an edit (JSON on stdin), the card stays waiting; --approve then approves it",
|
|
178
180
|
" cockpit approvals reject <id> decline it; nothing is sent",
|
|
179
181
|
" cockpit approvals rules [--active true|false] [--limit <n>]",
|
|
@@ -224,10 +226,10 @@ export const TOWER_OPS_COMMAND_HELP = [
|
|
|
224
226
|
" \"YYYY-MM-DD: \" dates a bullet, without it the bullet is undated. none clears --next or --blocker. blocked needs a blocker (given, or already on the card),",
|
|
225
227
|
" and a blocker goes only on a Blocked card: sent with any other health it is refused (summary_blocker_needs_blocked); sending only a new health drops the stored one.",
|
|
226
228
|
" --version is summary.updatedAt from `home <client> --json`, or none when the client has no card yet (then --health, --headline and at least one --bullet are all needed).",
|
|
227
|
-
" Limits: headline 140 characters, each bullet 160, next and blocker 200; a longer text is refused here with its length. A day spelled with a month name at the start of a bullet (\"Oct 3: x\") is refused, never saved undated. --version none is for a client with no card: on one that has a card the write is refused with \"This client already has a card: pass its version\". The edit is
|
|
228
|
-
"
|
|
229
|
+
" Limits: headline 140 characters, each bullet 160, next and blocker 200; a longer text is refused here with its length. A day spelled with a month name at the start of a bullet (\"Oct 3: x\") is refused, never saved undated. --version none is for a client with no card: on one that has a card the write is refused with \"This client already has a card: pass its version\". The edit is stored as written by your agent",
|
|
230
|
+
" (source agent, \"Written by <you>'s agent\"): Tower's check never rewrites it, whatever changes; your agent's next edit, or a person's on the page, replaces it.",
|
|
229
231
|
" A saved card that says On track while an open action is more than 14 days overdue is saved anyway and prints a \"Warning: ...\" line naming the action overdue longest.",
|
|
230
|
-
" Write the card after your item writes:
|
|
232
|
+
" Write the card after your item writes: the check will not rewrite it to take them in.",
|
|
231
233
|
" Health rule: an open action more than 14 days overdue, as `cockpit manager home` shows it (Tower's today, America/Vancouver), means the card is at_risk (or blocked, with a named blocker).",
|
|
232
234
|
" Do not set on_track over one; tell the person instead. The headline or a bullet must name the action overdue longest, with the day it was due.",
|
|
233
235
|
" cockpit manager list <client> [--timeline] [--needs-review] [--status open|done|dropped|closed|all] [--owner <name>|--no-owner] [--overdue] [--kind <kind>] [--search <text>] [--archived] [--review dismissed|all]",
|
|
@@ -259,7 +261,7 @@ export const TOWER_OPS_COMMAND_HELP = [
|
|
|
259
261
|
" --version and --timeline stay flags. A field given as a flag and in the file is refused, so is an unknown key. The file gets the flags' own checks. Example: cat fields.json | cockpit manager summary Acme --version none --fields-file -",
|
|
260
262
|
"Free text (the flags above and every value in a fields file) that holds a provider key (an sk-, rk_, sb_secret_, ghp_, github_pat_, AKIA/ASIA, xoxb- key, any JWT, a PEM private key) or a password given a value (\"password: x\") is refused, naming the flag or key; ordinary words, a commit sha, a URL and the words \"service-role key\" are plain text.",
|
|
261
263
|
"On a flag, free text (--headline, --bullet, --title, --detail, --next, --blocker, --owners, --source-note) goes in SINGLE quotes so the shell leaves $ alone: \"$12k\" in double quotes loses the $12k (zsh and bash read it as a variable). In bash and zsh write an apostrophe inside single quotes as '\\'' (end the quote, a backslash-escaped apostrophe, reopen); in PowerShell double the apostrophe ('Rowan''s deck'). The command prints what it saved: check it is your words.",
|
|
262
|
-
"<client> is the client's id, or its exact name (any case; `clients` lists them); a name that matches no client, or several, is refused with the names. Ids and versions are printed by list, queue and show. Items you add are
|
|
264
|
+
"<client> is the client's id, or its exact name (any case; `clients` lists them); a name that matches no client, or several, is refused with the names. Ids and versions are printed by list, queue and show. Items you add here are stored as added by your agent, not yet checked (they count in needs review until a person approves or dismisses them), never as you having checked them; text tagged [HR] is refused (item_hr_tagged, summary_hr_tagged).",
|
|
263
265
|
"--version is the item's version exactly as list or show prints it, copied as it is. Every change to an existing item names it: if someone or a sync changed the item",
|
|
264
266
|
"since, the write is refused with a sentence saying so and nothing is written; run show again for the new version and retry.",
|
|
265
267
|
"--owners on an action takes, as the web form says, \"One or more people, separated by commas\" (\"Rowan Ash, Juniper Twig\"), and it REPLACES the whole list. Leave it out and the owners stay;",
|