@bli-cockpit/cli 0.2.143 → 0.2.144
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.
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { askAgentDoor, emitAgentDoor, failAgentDoor } from "./agent-door.js";
|
|
2
|
+
import { writeLine } from "./cli-io.js";
|
|
3
|
+
const TAG = "[cal cli]";
|
|
4
|
+
const DELETE_DEADLINE_MS = 60_000;
|
|
5
|
+
export async function runCalDelete(command, door) {
|
|
6
|
+
const answer = await askAgentDoor(door, {
|
|
7
|
+
path: `/api/cal/events/${encodeURIComponent(command.subjectId ?? "")}`,
|
|
8
|
+
method: "DELETE",
|
|
9
|
+
label: "cal delete",
|
|
10
|
+
timeoutMs: DELETE_DEADLINE_MS,
|
|
11
|
+
});
|
|
12
|
+
if (!answer.ok)
|
|
13
|
+
return failAgentDoor(door, TAG, answer.reason, answer.detail);
|
|
14
|
+
const deleted = answer.body.deleted;
|
|
15
|
+
if (door.json)
|
|
16
|
+
return emitAgentDoor(door, { ok: true, deleted });
|
|
17
|
+
if (!deleted) {
|
|
18
|
+
writeLine(door.io.stdout, "Tower answered without a result. Nothing to report.");
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
const alreadyGone = deleted.google === "already_gone_at_google";
|
|
22
|
+
const stored = deleted.local_store === "removed"
|
|
23
|
+
? "Tower's copy is removed."
|
|
24
|
+
: "Tower's copy is still there for now — the next sync will pick up that it is gone.";
|
|
25
|
+
writeLine(door.io.stdout, alreadyGone
|
|
26
|
+
? `Google already had that event gone. ${stored}`
|
|
27
|
+
: `Deleted at Google${deleted.notified === "all" ? " — attendees were notified." : "."} ${stored}`);
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
package/dist/commands/cal.js
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* computed in UTC and "today" would end at 5pm in Vancouver.
|
|
22
22
|
*/
|
|
23
23
|
import { askAgentDoor, emitAgentDoor, failAgentDoor, openAgentDoor, } from "./agent-door.js";
|
|
24
|
+
import { runCalDelete } from "./cal-delete.js";
|
|
24
25
|
import { isInteractiveStdin, readPipedText, writeLine } from "./cli-io.js";
|
|
25
26
|
const TAG = "[cal cli]";
|
|
26
27
|
const READ_DEADLINE_MS = 30_000;
|
|
@@ -49,6 +50,8 @@ export async function runCal(command, io) {
|
|
|
49
50
|
return detach(command, door);
|
|
50
51
|
case "sync":
|
|
51
52
|
return sync(command, door);
|
|
53
|
+
case "delete":
|
|
54
|
+
return runCalDelete(command, door);
|
|
52
55
|
}
|
|
53
56
|
}
|
|
54
57
|
/**
|
|
@@ -30,9 +30,10 @@ const CAL_ACTIONS = new Set([
|
|
|
30
30
|
"share",
|
|
31
31
|
"detach",
|
|
32
32
|
"sync",
|
|
33
|
+
"delete",
|
|
33
34
|
]);
|
|
34
|
-
/** Every verb that names ONE calendar
|
|
35
|
-
const CAL_ACTIONS_NEEDING_A_SUBJECT = new Set(["sync", "detach", "share"]);
|
|
35
|
+
/** Every verb that names ONE thing first — a calendar id, or (delete) an event id. */
|
|
36
|
+
const CAL_ACTIONS_NEEDING_A_SUBJECT = new Set(["sync", "detach", "share", "delete"]);
|
|
36
37
|
export function parseCalArgs(args) {
|
|
37
38
|
const values = parseNamedArgs(args, {
|
|
38
39
|
allowedFlags: [
|
|
@@ -57,6 +58,7 @@ export function parseCalArgs(args) {
|
|
|
57
58
|
"--org-visible",
|
|
58
59
|
"--private",
|
|
59
60
|
"--full",
|
|
61
|
+
"--yes",
|
|
60
62
|
"--json",
|
|
61
63
|
],
|
|
62
64
|
valueFlags: [
|
|
@@ -81,6 +83,7 @@ export function parseCalArgs(args) {
|
|
|
81
83
|
const { subject, query } = readCalSubjectOrQuery(action, rest);
|
|
82
84
|
const { title, startsAt, endsAt } = readCalCreateDetails(action, values);
|
|
83
85
|
validateCalSharing(action, values);
|
|
86
|
+
validateCalDelete(action, values);
|
|
84
87
|
// Read in the order the flags have always been checked, so a caller who got
|
|
85
88
|
// two of them wrong is told about --limit first, as before.
|
|
86
89
|
const limit = optionalPositiveInteger(values.flags.get("--limit"), "--limit");
|
|
@@ -91,7 +94,7 @@ function readCalAction(values) {
|
|
|
91
94
|
const first = values.positionals[0];
|
|
92
95
|
const action = (first === undefined ? "today" : first);
|
|
93
96
|
if (!CAL_ACTIONS.has(action)) {
|
|
94
|
-
throw new Error(`Unknown cal command: ${first}. Try today, week, next, find, calendars, add-ical, create, share, detach, or
|
|
97
|
+
throw new Error(`Unknown cal command: ${first}. Try today, week, next, find, calendars, add-ical, create, share, detach, sync, or delete.`);
|
|
95
98
|
}
|
|
96
99
|
return { action, rest: values.positionals.slice(first === undefined ? 0 : 1) };
|
|
97
100
|
}
|
|
@@ -100,8 +103,11 @@ function readCalSubjectOrQuery(action, rest) {
|
|
|
100
103
|
let query;
|
|
101
104
|
if (CAL_ACTIONS_NEEDING_A_SUBJECT.has(action)) {
|
|
102
105
|
subject = optionalNonEmpty(rest[0]);
|
|
103
|
-
if (!subject)
|
|
104
|
-
throw new Error(
|
|
106
|
+
if (!subject) {
|
|
107
|
+
throw new Error(action === "delete"
|
|
108
|
+
? "cal delete needs an event id — take one from a window verb's output (today/week/next/find)."
|
|
109
|
+
: `cal ${action} needs a calendar id — take one from \`cockpit cal calendars\`.`);
|
|
110
|
+
}
|
|
105
111
|
if (rest.length > 1)
|
|
106
112
|
throw new Error(`cal ${action} takes one id, not ${rest.length}.`);
|
|
107
113
|
}
|
|
@@ -139,6 +145,12 @@ function validateCalSharing(action, values) {
|
|
|
139
145
|
throw new Error("cal share needs --org-visible (every member may read it) or --private (only you). Sharing is a decision, not a default.");
|
|
140
146
|
}
|
|
141
147
|
}
|
|
148
|
+
/** `delete` notifies attendees, exactly like `create`'s invitations — never without saying so first. */
|
|
149
|
+
function validateCalDelete(action, values) {
|
|
150
|
+
if (action === "delete" && !values.booleans.has("--yes")) {
|
|
151
|
+
throw new Error("cal delete needs --yes: it removes the event at Google and can email everybody on it. Add --yes once you mean it.");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
142
154
|
function readCalOffset(values) {
|
|
143
155
|
const offsetRaw = optionalNonEmpty(values.flags.get("--offset"));
|
|
144
156
|
const offset = offsetRaw === undefined ? undefined : Number.parseInt(offsetRaw, 10);
|
|
@@ -178,6 +190,7 @@ function buildCalCommand(values, action, subject, query, title, startsAt, endsAt
|
|
|
178
190
|
orgVisible: values.booleans.has("--org-visible"),
|
|
179
191
|
makePrivate: values.booleans.has("--private"),
|
|
180
192
|
full: values.booleans.has("--full"),
|
|
193
|
+
yes: values.booleans.has("--yes"),
|
|
181
194
|
json: values.booleans.has("--json"),
|
|
182
195
|
};
|
|
183
196
|
}
|
|
@@ -131,7 +131,7 @@ export const TOWER_COMMAND_HELP = [
|
|
|
131
131
|
[
|
|
132
132
|
"cal",
|
|
133
133
|
[
|
|
134
|
-
'Usage: cockpit cal [today|week|next|find "<words>"|calendars|add-ical|create|share <id>|sync <id>|detach <id>] [flags]',
|
|
134
|
+
'Usage: cockpit cal [today|week|next|find "<words>"|calendars|add-ical|create|share <id>|sync <id>|detach <id>|delete <event id> --yes] [flags]',
|
|
135
135
|
"",
|
|
136
136
|
"Every calendar you attached, in one place — plus any calendar a colleague marked shared.",
|
|
137
137
|
"today [--offset 1] [--tz <zone>] — what is on today, in YOUR zone (this machine's, unless --tz says otherwise). --offset 1 is tomorrow, -1 yesterday.",
|
|
@@ -149,8 +149,9 @@ export const TOWER_COMMAND_HELP = [
|
|
|
149
149
|
"share <id> --org-visible|--private — makes one of YOUR calendars readable by everyone at BLI, or private again. Sharing never lets anybody else change it.",
|
|
150
150
|
"sync <id> [--full] — reads that calendar now instead of waiting for the cron. --full ignores the sync token / ETag and re-reads everything.",
|
|
151
151
|
"detach <id> — removes the calendar and its stored events from Tower. The calendar itself is untouched at Google.",
|
|
152
|
+
"delete <event id> --yes — removes ONE event, AT GOOGLE first, then here. Refuses without --yes: a delete can email everybody on it, exactly like create's invitations do. Google already having it gone (404/410) is answered as success, not a refusal.",
|
|
152
153
|
"--json writes one machine-readable object to stdout; every reason and receipt line stays on stderr.",
|
|
153
|
-
"A refusal keeps Tower's own reason label — needs_rls_client, calendar_not_found_or_unreadable, calendar_not_yours, ical_url_invalid, google_oauth_not_configured, write_not_supported_for_provider, and so on.",
|
|
154
|
+
"A refusal keeps Tower's own reason label — needs_rls_client, calendar_not_found_or_unreadable, calendar_not_yours, event_not_found_or_unreadable, ical_url_invalid, google_oauth_not_configured, write_not_supported_for_provider, and so on.",
|
|
154
155
|
"The command uses the existing paired device identity. Run `cockpit login` first if this machine is not paired.",
|
|
155
156
|
],
|
|
156
157
|
],
|
|
@@ -21,7 +21,7 @@ export async function runCockpitCli(argv, io) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
if (command === "--version" || command === "-V" || command === "version") {
|
|
24
|
-
writeLine(io?.stdout ?? process.stdout, "0.2.
|
|
24
|
+
writeLine(io?.stdout ?? process.stdout, "0.2.144");
|
|
25
25
|
return 0;
|
|
26
26
|
}
|
|
27
27
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bli-cockpit/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.144",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@bli-cockpit/memory-mcp": "0.1.36",
|
|
31
|
-
"@bli-cockpit/mcp": "0.1.
|
|
31
|
+
"@bli-cockpit/mcp": "0.1.69",
|
|
32
32
|
"@bli-cockpit/telemetry-core": "0.1.48"
|
|
33
33
|
}
|
|
34
34
|
}
|