@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
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a person may type at `cockpit notes` (BLI-3459, BLI-4383, BLI-4394): the
|
|
3
|
+
* argument table for the meeting-notes surface, split out of
|
|
4
|
+
* `local-args-tower-pages.ts` when that file crossed the 700-line ceiling.
|
|
5
|
+
* Every flag, default and refusal sentence moved verbatim; `parseNotesArgs` and
|
|
6
|
+
* `NotesAction` are still exported from `./local-args-tower-pages.js`, the
|
|
7
|
+
* address they have always had.
|
|
8
|
+
*/
|
|
9
|
+
import { optionalNonEmpty, optionalPositiveInteger, optionalUrl, parseNamedArgs, } from "./local-arg-values.js";
|
|
10
|
+
/**
|
|
11
|
+
* The notetakers `cockpit notes connect` will actually seal a key for
|
|
12
|
+
* (BLI-4391). It mirrors `CONNECTABLE_NOTES_PROVIDERS` in the dashboard, and
|
|
13
|
+
* the door is still the authority — this list only saves a round trip and a
|
|
14
|
+
* sealed key for a provider with no reader.
|
|
15
|
+
*
|
|
16
|
+
* `otter` and `granola` are deliberately absent: Otter's public API is
|
|
17
|
+
* Enterprise-only and Granola still runs on its own server-side cron.
|
|
18
|
+
*/
|
|
19
|
+
const CONNECTABLE_PROVIDERS = ["fellow", "circleback", "wisprflow"];
|
|
20
|
+
/**
|
|
21
|
+
* BLI-12926. Providers with no API key at all: the person signs in through
|
|
22
|
+
* their browser and `connect` runs that sign-in itself. `--key-stdin` is
|
|
23
|
+
* optional for these, and carries a pre-built sign-in bundle when given.
|
|
24
|
+
*/
|
|
25
|
+
const PROVIDERS_SIGNED_IN_BY_BROWSER = ["wisprflow"];
|
|
26
|
+
/** The provider whose API is per workspace, so its account needs a subdomain. */
|
|
27
|
+
const PROVIDERS_NEEDING_A_WORKSPACE = ["fellow"];
|
|
28
|
+
const NOTES_ACTIONS = new Set([
|
|
29
|
+
"folders", "mkdir", "rmdir", "rename",
|
|
30
|
+
"list",
|
|
31
|
+
"show",
|
|
32
|
+
"shelf",
|
|
33
|
+
"shelves",
|
|
34
|
+
"upload",
|
|
35
|
+
"paste",
|
|
36
|
+
"share",
|
|
37
|
+
"unshare",
|
|
38
|
+
"summarize",
|
|
39
|
+
"move",
|
|
40
|
+
"place",
|
|
41
|
+
// BLI-4383: the notetaker a person connects, and the meetings it records.
|
|
42
|
+
"accounts",
|
|
43
|
+
"connect",
|
|
44
|
+
"detach",
|
|
45
|
+
"sync",
|
|
46
|
+
"sync-status",
|
|
47
|
+
// BLI-12926: the alternates of a meeting several notetakers recorded.
|
|
48
|
+
"recordings",
|
|
49
|
+
"recording",
|
|
50
|
+
// BLI-4394: arm that notetaker's webhook with the vendor's signing secret.
|
|
51
|
+
"webhook",
|
|
52
|
+
// Ticket 10718: the Granola cron's own last-several-runs health.
|
|
53
|
+
"granola-status",
|
|
54
|
+
"transcription-stats",
|
|
55
|
+
"bot-upcoming",
|
|
56
|
+
]);
|
|
57
|
+
/** Actions whose first positional is the note it acts on. */
|
|
58
|
+
const NOTES_ACTIONS_NEEDING_A_NOTE = new Set([
|
|
59
|
+
"show",
|
|
60
|
+
"share",
|
|
61
|
+
"unshare",
|
|
62
|
+
"summarize",
|
|
63
|
+
"move",
|
|
64
|
+
"place",
|
|
65
|
+
// BLI-12926: `recordings` names the note, `recording` names the recording.
|
|
66
|
+
"recordings",
|
|
67
|
+
"recording",
|
|
68
|
+
]);
|
|
69
|
+
/**
|
|
70
|
+
* BLI-4383. Actions whose first positional is a NOTETAKER ACCOUNT id, not a
|
|
71
|
+
* note id. `sync` takes one too, and is handled beside these rather than in
|
|
72
|
+
* this set because its id is checked at the door instead of here.
|
|
73
|
+
*/
|
|
74
|
+
const NOTES_ACTIONS_NEEDING_AN_ACCOUNT = new Set(["detach", "webhook"]);
|
|
75
|
+
const NOTES_ALLOWED_FLAGS = [
|
|
76
|
+
"--wait",
|
|
77
|
+
"--home",
|
|
78
|
+
"--dashboard-url",
|
|
79
|
+
"--file",
|
|
80
|
+
"--name",
|
|
81
|
+
"--exclude",
|
|
82
|
+
"--to",
|
|
83
|
+
"--folder",
|
|
84
|
+
"--clear-shelf",
|
|
85
|
+
"--apply",
|
|
86
|
+
"--series",
|
|
87
|
+
"--kind",
|
|
88
|
+
"--since",
|
|
89
|
+
"--until",
|
|
90
|
+
"--limit",
|
|
91
|
+
"--month",
|
|
92
|
+
"--offset",
|
|
93
|
+
"--yes",
|
|
94
|
+
"--json",
|
|
95
|
+
"--tree",
|
|
96
|
+
// BLI-4383. `--key-stdin` is a FLAG and not a value: the key itself is
|
|
97
|
+
// read from stdin and never from argv.
|
|
98
|
+
"--provider",
|
|
99
|
+
"--key-stdin",
|
|
100
|
+
// BLI-4391. Fellow's API is per WORKSPACE, and a subdomain is a label,
|
|
101
|
+
// not a secret, so this one carries a value while the key never does.
|
|
102
|
+
"--workspace",
|
|
103
|
+
// BLI-4394. `--secret-stdin` is a FLAG and not a value, for the same
|
|
104
|
+
// reason `--key-stdin` is: the signing secret never touches argv.
|
|
105
|
+
"--secret-stdin",
|
|
106
|
+
"--show-webhook",
|
|
107
|
+
// BLI-18006. `sync <id> --repair-truncated` re-reads Wispr Flow notes whose
|
|
108
|
+
// transcript was stored cut short. A FLAG, not a verb: it is the same sync
|
|
109
|
+
// door with a different request.
|
|
110
|
+
"--repair-truncated",
|
|
111
|
+
];
|
|
112
|
+
const NOTES_VALUE_FLAGS = [
|
|
113
|
+
"--home",
|
|
114
|
+
"--dashboard-url",
|
|
115
|
+
"--file",
|
|
116
|
+
"--name",
|
|
117
|
+
"--exclude",
|
|
118
|
+
"--to",
|
|
119
|
+
"--folder",
|
|
120
|
+
"--series",
|
|
121
|
+
"--kind",
|
|
122
|
+
"--since",
|
|
123
|
+
"--until",
|
|
124
|
+
"--limit",
|
|
125
|
+
"--month",
|
|
126
|
+
"--offset",
|
|
127
|
+
"--provider",
|
|
128
|
+
"--workspace",
|
|
129
|
+
"--show-webhook",
|
|
130
|
+
];
|
|
131
|
+
export function parseNotesArgs(args) {
|
|
132
|
+
const values = parseNamedArgs(args, { allowedFlags: NOTES_ALLOWED_FLAGS, valueFlags: NOTES_VALUE_FLAGS });
|
|
133
|
+
const action = readNotesAction(values.positionals[0]);
|
|
134
|
+
const rest = values.positionals.slice(values.positionals[0] === undefined ? 0 : 1);
|
|
135
|
+
const { json, yes } = readNotesConfirmation(values);
|
|
136
|
+
const noteId = readNotesNoteId(action, rest);
|
|
137
|
+
const showWebhook = optionalNonEmpty(values.flags.get("--show-webhook"));
|
|
138
|
+
const accountId = readNotesAccountId(action, rest, showWebhook);
|
|
139
|
+
requireWebhookSecretOnlyForWebhook(action, values);
|
|
140
|
+
const provider = optionalNonEmpty(values.flags.get("--provider"));
|
|
141
|
+
const workspace = optionalNonEmpty(values.flags.get("--workspace"));
|
|
142
|
+
if (action === "connect")
|
|
143
|
+
rejectBadConnectRequest(values, provider, workspace, rest);
|
|
144
|
+
const paths = readNotesPaths(action, rest);
|
|
145
|
+
if (action === "rename" && !optionalNonEmpty(values.flags.get("--name")))
|
|
146
|
+
throw new Error("notes rename needs --name <name>.");
|
|
147
|
+
const { to, clearShelf } = readNotesShelfTarget(action, values);
|
|
148
|
+
const repairTruncated = readRepairTruncated(action, values);
|
|
149
|
+
const { limit, month, offset } = readNotesWindow(action, values);
|
|
150
|
+
return {
|
|
151
|
+
kind: "notes",
|
|
152
|
+
...(values.booleans.has("--wait") ? { wait: true } : {}),
|
|
153
|
+
action,
|
|
154
|
+
...(values.flags.has("--folder") ? { folder: values.flags.get("--folder") } : {}),
|
|
155
|
+
...(action === "mkdir" || action === "rmdir" || action === "rename" ? { folder: rest[0] } : {}),
|
|
156
|
+
...(values.booleans.has("--tree") ? { tree: true } : {}),
|
|
157
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
158
|
+
dashboardUrl: optionalUrl(values.flags.get("--dashboard-url")),
|
|
159
|
+
...(noteId ? { noteId } : {}),
|
|
160
|
+
...(accountId ? { accountId } : {}),
|
|
161
|
+
...(provider ? { provider } : {}),
|
|
162
|
+
...(workspace ? { workspace } : {}),
|
|
163
|
+
...(showWebhook ? { showWebhook: true } : {}),
|
|
164
|
+
...(action === "connect" && values.booleans.has("--key-stdin") ? { keyStdin: true } : {}),
|
|
165
|
+
...(paths ? { paths } : {}),
|
|
166
|
+
filePath: optionalNonEmpty(values.flags.get("--file")),
|
|
167
|
+
name: optionalNonEmpty(values.flags.get("--name")),
|
|
168
|
+
exclude: optionalNonEmpty(values.flags.get("--exclude")),
|
|
169
|
+
...(to ? { to } : {}),
|
|
170
|
+
...(clearShelf ? { clearShelf } : {}),
|
|
171
|
+
// BLI-4058. `place` says where a note belongs; only `--apply` moves it.
|
|
172
|
+
...(values.booleans.has("--apply") ? { apply: true } : {}),
|
|
173
|
+
series: optionalNonEmpty(values.flags.get("--series")),
|
|
174
|
+
meetingKind: optionalNonEmpty(values.flags.get("--kind")),
|
|
175
|
+
since: optionalNonEmpty(values.flags.get("--since")),
|
|
176
|
+
until: optionalNonEmpty(values.flags.get("--until")),
|
|
177
|
+
...(limit === undefined ? {} : { limit }),
|
|
178
|
+
...(month ? { month } : {}),
|
|
179
|
+
...(offset === undefined ? {} : { offset }),
|
|
180
|
+
...(repairTruncated ? { repairTruncated: true } : {}),
|
|
181
|
+
yes,
|
|
182
|
+
json,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function readNotesAction(first) {
|
|
186
|
+
// Bare `cockpit notes` is the list, which is the thing a person typing it
|
|
187
|
+
// almost always wants — same reasoning as bare `cockpit` running convergence.
|
|
188
|
+
const action = (first === undefined ? "list" : first);
|
|
189
|
+
if (!NOTES_ACTIONS.has(action)) {
|
|
190
|
+
throw new Error(`Unknown notes command: ${first}. Try list, show, shelf, shelves, upload, paste, share, unshare, summarize, move, place, accounts, connect, detach, sync, sync-status, recordings, recording, webhook, granola-status, or transcription-stats.`);
|
|
191
|
+
}
|
|
192
|
+
return action;
|
|
193
|
+
}
|
|
194
|
+
function readRepairTruncated(action, values) {
|
|
195
|
+
const repairTruncated = values.booleans.has("--repair-truncated");
|
|
196
|
+
if (repairTruncated && action !== "sync") {
|
|
197
|
+
throw new Error("--repair-truncated belongs to `cockpit notes sync <account-id> --repair-truncated`.");
|
|
198
|
+
}
|
|
199
|
+
return repairTruncated;
|
|
200
|
+
}
|
|
201
|
+
function readNotesConfirmation(values) {
|
|
202
|
+
const json = values.booleans.has("--json");
|
|
203
|
+
// `--json` implies `--yes`: a machine-readable run has nobody to prompt, and
|
|
204
|
+
// failing it for want of a confirmation flag it cannot see is a worse
|
|
205
|
+
// surprise than an explicit share it already asked for by name.
|
|
206
|
+
return { json, yes: values.booleans.has("--yes") || json };
|
|
207
|
+
}
|
|
208
|
+
/** How much of what to read: `--limit`, the transcription-stats month, the `show` offset. */
|
|
209
|
+
function readNotesWindow(action, values) {
|
|
210
|
+
const limit = optionalPositiveInteger(values.flags.get("--limit"), "--limit");
|
|
211
|
+
const month = readNotesMonth(action, values);
|
|
212
|
+
const offset = readNotesOffset(action, values);
|
|
213
|
+
return { limit, month, offset };
|
|
214
|
+
}
|
|
215
|
+
function readNotesNoteId(action, rest) {
|
|
216
|
+
if (!NOTES_ACTIONS_NEEDING_A_NOTE.has(action))
|
|
217
|
+
return undefined;
|
|
218
|
+
const noteId = optionalNonEmpty(rest[0]);
|
|
219
|
+
if (!noteId)
|
|
220
|
+
throw new Error(`notes ${action} needs a note id.`);
|
|
221
|
+
if (rest.length > 1) {
|
|
222
|
+
throw new Error(`notes ${action} takes one note id, not ${rest.length}.`);
|
|
223
|
+
}
|
|
224
|
+
return noteId;
|
|
225
|
+
}
|
|
226
|
+
/** The notetaker account a verb names; `--show-webhook <id>` names one on `accounts`. */
|
|
227
|
+
function readNotesAccountId(action, rest, showWebhook) {
|
|
228
|
+
let accountId;
|
|
229
|
+
if (NOTES_ACTIONS_NEEDING_AN_ACCOUNT.has(action) || action === "sync" || action === "sync-status") {
|
|
230
|
+
accountId = optionalNonEmpty(rest[0]);
|
|
231
|
+
if (!accountId && NOTES_ACTIONS_NEEDING_AN_ACCOUNT.has(action)) {
|
|
232
|
+
throw new Error(`notes ${action} needs a notetaker account id.`);
|
|
233
|
+
}
|
|
234
|
+
if (rest.length > 1) {
|
|
235
|
+
throw new Error(`notes ${action} takes one account id, not ${rest.length}.`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// BLI-4394. `--show-webhook <id>` is a flag on `accounts` rather than a verb
|
|
239
|
+
// of its own, because "what is this one's webhook URL?" is the question a
|
|
240
|
+
// person asks WHILE looking at the list.
|
|
241
|
+
if (showWebhook === undefined)
|
|
242
|
+
return accountId;
|
|
243
|
+
if (action !== "accounts") {
|
|
244
|
+
throw new Error("--show-webhook belongs to `cockpit notes accounts --show-webhook <account-id>`.");
|
|
245
|
+
}
|
|
246
|
+
return showWebhook;
|
|
247
|
+
}
|
|
248
|
+
function requireWebhookSecretOnlyForWebhook(action, values) {
|
|
249
|
+
if (action !== "webhook") {
|
|
250
|
+
if (values.booleans.has("--secret-stdin")) {
|
|
251
|
+
throw new Error("--secret-stdin belongs to `cockpit notes webhook <account-id> --secret-stdin`.");
|
|
252
|
+
}
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (!values.booleans.has("--secret-stdin")) {
|
|
256
|
+
// Naming the flag is the point: it says out loud that the signing secret
|
|
257
|
+
// is not an argument, so nobody goes looking for a `--secret` that will
|
|
258
|
+
// never exist.
|
|
259
|
+
throw new Error('notes webhook reads the signing secret from stdin. Say so: printf "%s" "<secret>" | cockpit notes webhook <account-id> --secret-stdin');
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function rejectBadConnectRequest(values, provider, workspace, rest) {
|
|
263
|
+
if (!provider) {
|
|
264
|
+
throw new Error(`notes connect needs --provider ${CONNECTABLE_PROVIDERS.join("|")}.`);
|
|
265
|
+
}
|
|
266
|
+
if (!CONNECTABLE_PROVIDERS.includes(provider)) {
|
|
267
|
+
throw new Error(`notes connect does not know the provider "${provider}". Tower reads ${CONNECTABLE_PROVIDERS.join(" and ")}.`);
|
|
268
|
+
}
|
|
269
|
+
if (PROVIDERS_NEEDING_A_WORKSPACE.includes(provider) && !workspace) {
|
|
270
|
+
throw new Error(`notes connect --provider ${provider} needs --workspace <subdomain>: its API lives at https://<subdomain>.fellow.app, so a key alone cannot address anything.`);
|
|
271
|
+
}
|
|
272
|
+
if (workspace !== undefined && !/^[a-z0-9-]{1,63}$/.test(workspace)) {
|
|
273
|
+
throw new Error("--workspace is a workspace subdomain: lowercase letters, digits and hyphens, 1 to 63 characters.");
|
|
274
|
+
}
|
|
275
|
+
if (!values.booleans.has("--key-stdin") && !PROVIDERS_SIGNED_IN_BY_BROWSER.includes(provider)) {
|
|
276
|
+
// Naming the flag is the point: it says out loud that the key is not an
|
|
277
|
+
// argument, so nobody goes looking for a `--key` that will never exist.
|
|
278
|
+
throw new Error('notes connect reads the key from stdin. Say so: printf "%s" "<key>" | cockpit notes connect --provider fellow --workspace <subdomain> --key-stdin');
|
|
279
|
+
}
|
|
280
|
+
if (rest.length > 0)
|
|
281
|
+
throw new Error(`notes connect does not take "${rest[0]}".`);
|
|
282
|
+
}
|
|
283
|
+
/** The upload paths, and a refusal for stray positionals on verbs that take none. */
|
|
284
|
+
function readNotesPaths(action, rest) {
|
|
285
|
+
if (action === "upload") {
|
|
286
|
+
// Explicit paths only. No globbing happens here — a shell that expands
|
|
287
|
+
// `*.md` hands us the names it found, and a shell that does not would have
|
|
288
|
+
// this command silently uploading a file literally called `*.md`.
|
|
289
|
+
const paths = rest.filter((value) => value.trim() !== "");
|
|
290
|
+
if (paths.length === 0)
|
|
291
|
+
throw new Error("notes upload needs at least one file path.");
|
|
292
|
+
return paths;
|
|
293
|
+
}
|
|
294
|
+
if (action === "mkdir" || action === "rmdir" || action === "rename") {
|
|
295
|
+
if (rest.length !== 1 || !rest[0]?.trim())
|
|
296
|
+
throw new Error(`notes ${action} needs one folder path.`);
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
const takesNoPositional = action !== "paste"
|
|
300
|
+
&& action !== "connect"
|
|
301
|
+
&& action !== "sync"
|
|
302
|
+
&& action !== "sync-status"
|
|
303
|
+
&& !NOTES_ACTIONS_NEEDING_A_NOTE.has(action)
|
|
304
|
+
&& !NOTES_ACTIONS_NEEDING_AN_ACCOUNT.has(action);
|
|
305
|
+
if (takesNoPositional && rest.length > 0) {
|
|
306
|
+
throw new Error(`notes ${action} does not take "${rest[0]}".`);
|
|
307
|
+
}
|
|
308
|
+
return undefined;
|
|
309
|
+
}
|
|
310
|
+
function readNotesShelfTarget(action, values) {
|
|
311
|
+
const to = optionalNonEmpty(values.flags.get("--to"));
|
|
312
|
+
const clearShelf = values.booleans.has("--clear-shelf");
|
|
313
|
+
if (action !== "move")
|
|
314
|
+
return { to, clearShelf };
|
|
315
|
+
if (to && clearShelf) {
|
|
316
|
+
throw new Error("notes move accepts either --to or --clear-shelf, not both.");
|
|
317
|
+
}
|
|
318
|
+
if (!to && !clearShelf && !values.flags.get("--folder")) {
|
|
319
|
+
throw new Error('notes move needs --to "<shelf>", --clear-shelf, or --folder "<path>".');
|
|
320
|
+
}
|
|
321
|
+
return { to, clearShelf };
|
|
322
|
+
}
|
|
323
|
+
function readNotesMonth(action, values) {
|
|
324
|
+
const month = optionalNonEmpty(values.flags.get("--month"));
|
|
325
|
+
if (month === undefined)
|
|
326
|
+
return undefined;
|
|
327
|
+
if (action !== "transcription-stats") {
|
|
328
|
+
throw new Error("--month belongs to `cockpit notes transcription-stats --month YYYY-MM`.");
|
|
329
|
+
}
|
|
330
|
+
if (!/^\d{4}-(0[1-9]|1[0-2])$/.test(month)) {
|
|
331
|
+
throw new Error("--month is a calendar month like 2026-10.");
|
|
332
|
+
}
|
|
333
|
+
return month;
|
|
334
|
+
}
|
|
335
|
+
/** BLI-18006: a long note is read in parts, like `cockpit docs read --offset`. */
|
|
336
|
+
function readNotesOffset(action, values) {
|
|
337
|
+
const offsetRaw = optionalNonEmpty(values.flags.get("--offset"));
|
|
338
|
+
if (offsetRaw === undefined)
|
|
339
|
+
return undefined;
|
|
340
|
+
if (action !== "show")
|
|
341
|
+
throw new Error("--offset belongs to `cockpit notes show <id> --offset <characters> --limit <characters>`.");
|
|
342
|
+
const offset = Number(offsetRaw);
|
|
343
|
+
if (!Number.isInteger(offset) || offset < 0)
|
|
344
|
+
throw new Error("--offset must be a whole number 0 or greater.");
|
|
345
|
+
return offset;
|
|
346
|
+
}
|