@bli-cockpit/cli 0.2.237 → 0.2.239
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-direct.js +119 -0
- package/dist/commands/cal-overlay.js +73 -30
- package/dist/commands/cal.js +62 -25
- package/dist/commands/clean.js +4 -1
- package/dist/commands/docs-read.js +5 -2
- package/dist/commands/docs.js +38 -6
- 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-versions.js +93 -0
- package/dist/commands/issue-write.js +70 -23
- package/dist/commands/issue.js +73 -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 +69 -54
- package/dist/commands/local-args-tower-careers.js +73 -54
- package/dist/commands/local-args-tower-docs-msg.js +79 -51
- 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-msg.js +24 -6
- 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-setup.js +11 -4
- package/dist/commands/local-args-tower-work.js +74 -37
- 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 +20 -7
- 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/msg-read-line.js +32 -0
- package/dist/commands/msg.js +45 -3
- 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/setup.js +13 -0
- 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 +94 -68
- 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/spool/local-spool-parse.js +13 -12
- 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 +4 -4
|
@@ -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>"]`
|
|
@@ -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", "duplicate", "batch", "undo", "conflicts", "calendar-change",
|
|
24
24
|
"today",
|
|
25
25
|
"week",
|
|
26
26
|
"next",
|
|
@@ -36,58 +36,67 @@ 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", "duplicate", "calendar-change", "sync", "detach", "share", "delete", "change", "cancel"]);
|
|
40
40
|
/** `sync-status [<calendar id>]` — every calendar, or one's. Ticket 10730. */
|
|
41
|
-
const CAL_ACTIONS_WITH_OPTIONAL_SUBJECT = new Set(["sync-status"]);
|
|
41
|
+
const CAL_ACTIONS_WITH_OPTIONAL_SUBJECT = new Set(["sync-status", "undo"]);
|
|
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
|
+
"--timed",
|
|
64
|
+
"--keep-invitees",
|
|
65
|
+
"--meet",
|
|
66
|
+
"--org-visible",
|
|
67
|
+
"--private",
|
|
68
|
+
"--full",
|
|
69
|
+
"--yes",
|
|
70
|
+
"--json",
|
|
71
|
+
"--idempotency-key",
|
|
72
|
+
"--expected-version",
|
|
73
|
+
"--reason",
|
|
74
|
+
];
|
|
75
|
+
const CAL_VALUE_FLAGS = [
|
|
76
|
+
"--home",
|
|
77
|
+
"--dashboard-url",
|
|
78
|
+
"--color",
|
|
79
|
+
"--calendar",
|
|
80
|
+
"--account",
|
|
81
|
+
"--tz",
|
|
82
|
+
"--occurrence-start",
|
|
83
|
+
"--offset",
|
|
84
|
+
"--hours",
|
|
85
|
+
"--from",
|
|
86
|
+
"--to",
|
|
87
|
+
"--limit",
|
|
88
|
+
"--name",
|
|
89
|
+
"--title",
|
|
90
|
+
"--at",
|
|
91
|
+
"--until",
|
|
92
|
+
"--location",
|
|
93
|
+
"--attendee",
|
|
94
|
+
"--idempotency-key",
|
|
95
|
+
"--expected-version",
|
|
96
|
+
"--reason",
|
|
97
|
+
];
|
|
42
98
|
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
|
-
});
|
|
99
|
+
const values = parseNamedArgs(args, { allowedFlags: CAL_ALLOWED_FLAGS, valueFlags: CAL_VALUE_FLAGS });
|
|
91
100
|
const { action, rest } = readCalAction(values);
|
|
92
101
|
const { subject, query } = readCalSubjectOrQuery(action, rest);
|
|
93
102
|
const { title, startsAt, endsAt } = readCalCreateDetails(action, values);
|
|
@@ -103,7 +112,7 @@ function readCalAction(values) {
|
|
|
103
112
|
const first = values.positionals[0];
|
|
104
113
|
const action = (first === undefined ? "today" : first);
|
|
105
114
|
if (!CAL_ACTIONS.has(action)) {
|
|
106
|
-
throw new Error(`Unknown cal command: ${first}. Try today, week, conflicts, calendar-change, next, find, calendars, add-ical, create, share, detach, sync, sync-status, or delete.`);
|
|
115
|
+
throw new Error(`Unknown cal command: ${first}. Try today, week, conflicts, calendar-change, next, find, calendars, add-ical, create, duplicate, batch, undo, share, detach, sync, sync-status, or delete.`);
|
|
107
116
|
}
|
|
108
117
|
return { action, rest: values.positionals.slice(first === undefined ? 0 : 1) };
|
|
109
118
|
}
|
|
@@ -113,8 +122,8 @@ function readCalSubjectOrQuery(action, rest) {
|
|
|
113
122
|
if (CAL_ACTIONS_NEEDING_A_SUBJECT.has(action)) {
|
|
114
123
|
subject = optionalNonEmpty(rest[0]);
|
|
115
124
|
if (!subject) {
|
|
116
|
-
throw new Error(action === "delete"
|
|
117
|
-
?
|
|
125
|
+
throw new Error(action === "delete" || action === "duplicate"
|
|
126
|
+
? `cal ${action} needs an event id — take one from a window verb's output (today/week/next/find).`
|
|
118
127
|
: `cal ${action} needs a calendar id — take one from \`cockpit cal calendars\`.`);
|
|
119
128
|
}
|
|
120
129
|
if (rest.length > 1)
|
|
@@ -173,6 +182,7 @@ function readCalOffset(values) {
|
|
|
173
182
|
function buildCalCommand(values, action, subject, query, title, startsAt, endsAt, offset, limit) {
|
|
174
183
|
return {
|
|
175
184
|
kind: "cal",
|
|
185
|
+
occurrenceStart: optionalNonEmpty(values.flags.get("--occurrence-start")),
|
|
176
186
|
color: optionalNonEmpty(values.flags.get("--color")),
|
|
177
187
|
action,
|
|
178
188
|
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
@@ -199,6 +209,11 @@ function buildCalCommand(values, action, subject, query, title, startsAt, endsAt
|
|
|
199
209
|
location: optionalNonEmpty(values.flags.get("--location")),
|
|
200
210
|
attendees: splitAddresses(values.flags.get("--attendee")),
|
|
201
211
|
allDay: values.booleans.has("--all-day"),
|
|
212
|
+
timed: values.booleans.has("--timed"),
|
|
213
|
+
keepInvitees: values.booleans.has("--keep-invitees"),
|
|
214
|
+
idempotencyKey: optionalNonEmpty(values.flags.get("--idempotency-key")),
|
|
215
|
+
expectedVersion: optionalNonEmpty(values.flags.get("--expected-version")),
|
|
216
|
+
reason: optionalNonEmpty(values.flags.get("--reason")),
|
|
202
217
|
meet: values.booleans.has("--meet"),
|
|
203
218
|
orgVisible: values.booleans.has("--org-visible"),
|
|
204
219
|
makePrivate: values.booleans.has("--private"),
|