@wirecat/cli-core 0.457.0 → 0.458.0
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/CHANGELOG.md +36 -0
- package/dist/calendar/cli/index.d.ts +3 -0
- package/dist/calendar/cli/index.js +73 -2
- package/dist/calendar/index.d.ts +2 -0
- package/dist/calendar/index.js +1 -0
- package/dist/calendar/mcp/index.d.ts +750 -1
- package/dist/calendar/mcp/index.js +174 -5
- package/dist/calendar/reads.d.ts +16 -1
- package/dist/calendar/reads.js +16 -15
- package/dist/calendar/testing/index.d.ts +2 -0
- package/dist/calendar/testing/index.js +2 -0
- package/dist/calendar/testing/memory.js +5 -0
- package/dist/calendar/testing/writer-contract.d.ts +23 -0
- package/dist/calendar/testing/writer-contract.js +137 -0
- package/dist/calendar/testing/writer.d.ts +26 -0
- package/dist/calendar/testing/writer.js +101 -0
- package/dist/calendar/writer.d.ts +68 -0
- package/dist/calendar/writer.js +17 -0
- package/dist/calendar/writes.d.ts +128 -0
- package/dist/calendar/writes.js +237 -0
- package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
- package/dist/errors/reasons.js +2 -0
- package/dist/meetings/event-store.d.ts +4 -0
- package/dist/store/sqlite/calendar.js +7 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,42 @@ shape of this file.
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## 0.458.0 — 27.10.2026
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Calendar writes (`@wirecat/cli-core/calendar`).** An optional `CalendarWriter` beside `CalendarSource`:
|
|
15
|
+
`create(calendar, draft, { id, notify })`, `update(calendar, externalId, patch, { scope, etag, notify })`,
|
|
16
|
+
`remove(calendar, externalId, { scope, etag, notify })` and `respond(calendar, externalId, response, { etag,
|
|
17
|
+
notify })`, in neutral words (`EventDraft`, `EventPatch`, `WriteScope` `this | series`, `EventResponse`). The id of a
|
|
18
|
+
create is chosen before the call and journaled as its `sendId`, so a retry after `outcome_unknown` with the same id
|
|
19
|
+
finds the event instead of making a second one. Every change sends the stored `etag`; an adapter turns a mismatch
|
|
20
|
+
into `calendarConflict()` (`validation_error`, reason `event.conflict`: sync and try again), never an overwrite. What
|
|
21
|
+
a write returns is saved at once; a deletion cancels the entry as a sync would. Repeating events are not created
|
|
22
|
+
here, and "this and following" is not offered.
|
|
23
|
+
- **`events create | update | delete | respond`** in `addCalendarCommands`, only when the CLI passes
|
|
24
|
+
`writes: CalendarWrites` (the writer, `app`, the profile's `GuardSettings`, and `extraKeys(write)` for keys the
|
|
25
|
+
CLI adds, such as gcal's `api.calendar.events.insert`). Each goes through `guardFor` and the send journal as a
|
|
26
|
+
messenger's writes do (WC-PERM-13, WC-PERM-17), and `--dry-run` previews it. Built-in levels
|
|
27
|
+
(`CALENDAR_WRITE_DEFAULTS`, WC-PERM-6/7): `events.create`, `events.update`, `events.respond` and `events.delete`
|
|
28
|
+
`ask`; `events.delete.series` `deny`; `events.notify` `deny`. Times are `--start-time`/`--end-time`, an all-day
|
|
29
|
+
event `--start-date`/`--end-date` (the last day). `--notify` or `--no-notify` is required when the event has
|
|
30
|
+
attendees other than the owner (reason `event.notify-required`), and `--notify` must pass `events.notify`.
|
|
31
|
+
`CALENDAR_WRITE_KEYS` lists the keys that are not command paths, for `commandPermissions({ extra })`.
|
|
32
|
+
`addCalendarCommands` now honours a global `--dry-run` in any host program, as cli-core's other shared commands do.
|
|
33
|
+
- **`calendarWriteTools`** (`event_create`, `event_update`, `event_delete`, `event_respond`) for `callCalendarTool`
|
|
34
|
+
with `writes: (accountId) => CalendarWrites`. Over MCP `ask` goes ahead (WC-MCP-7); `deny`, `readonly`,
|
|
35
|
+
`events.notify` and the extra keys still refuse; `notify` is `true` or `false`. A write acts in the account named, or the only one that holds the
|
|
36
|
+
calendar or event; two make it ask for `account_id`.
|
|
37
|
+
- **Contract kit:** `fakeCalendarWriter` and `calendarWriterContract` (a stale etag is a conflict for update, remove
|
|
38
|
+
and respond; a create retried with its id finds the event; `notify` reaches the provider as given; with
|
|
39
|
+
`events.notify` at its default a create that would mail attendees never reaches the provider).
|
|
40
|
+
|
|
41
|
+
### Changed — may break callers
|
|
42
|
+
|
|
43
|
+
- **`EventStore` has `entry(calendarId, externalId)`, and `StoredEntry` has `etag`.** A store of its own must add
|
|
44
|
+
both; the column already exists, so there is no migration.
|
|
45
|
+
|
|
10
46
|
## 0.457.0 — 27.10.2026
|
|
11
47
|
|
|
12
48
|
### Fixed
|
|
@@ -2,6 +2,7 @@ import { type Command } from "commander";
|
|
|
2
2
|
import type { EventStore } from "../../meetings/index.js";
|
|
3
3
|
import type { AccountKey, MessageStore } from "../../store/store.js";
|
|
4
4
|
import type { CalendarExpander, CalendarSource } from "../source.js";
|
|
5
|
+
import { type CalendarWrites } from "../writes.js";
|
|
5
6
|
export interface CalendarCommandDeps {
|
|
6
7
|
store: EventStore;
|
|
7
8
|
/** The selected profile's calendar account: every command reads and syncs only it. */
|
|
@@ -20,5 +21,7 @@ export interface CalendarCommandDeps {
|
|
|
20
21
|
now?: () => number;
|
|
21
22
|
/** The zone `today`, `week` and a bare date are read in; the computer's when absent. */
|
|
22
23
|
timezone?: string;
|
|
24
|
+
/** The provider's writer and the profile's guard: `events create|update|delete|respond` exist only with it. */
|
|
25
|
+
writes?: CalendarWrites;
|
|
23
26
|
}
|
|
24
27
|
export declare const addCalendarCommands: (program: Command, deps: CalendarCommandDeps) => void;
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { CliError } from "@wirecat/cli-base";
|
|
2
2
|
import { annotate, sharedCommand } from "@wirecat/cli-base/commands";
|
|
3
3
|
import { timezoneOf } from "@wirecat/cli-base/query";
|
|
4
|
-
import { InvalidArgumentError } from "commander";
|
|
4
|
+
import { Argument, InvalidArgumentError } from "commander";
|
|
5
|
+
import { honoursDryRun } from "../../cli/dry-run-guard.js";
|
|
6
|
+
import { terminalAsker } from "../../cli/messenger/ask.js";
|
|
5
7
|
import { searchEvents } from "../../services/events-search.js";
|
|
6
8
|
import { linkPeople } from "../../services/people.js";
|
|
7
9
|
import { calendarIn, calendarItem, EVENTS_QUERY_HELP, eventAnswer, eventIdOf, eventOf, eventsAnswer, eventsFilter, peopleAnswer, resolveAnswer, reviewsAnswer, } from "../reads.js";
|
|
8
10
|
import { syncCalendars } from "../sync.js";
|
|
11
|
+
import { createEvent, deleteEvent, EVENT_RESPONSES, respondToEvent, updateEvent, } from "../writes.js";
|
|
9
12
|
const LOCAL_READ = { remote: "none", local: "read" };
|
|
10
13
|
const LOCAL_WRITE = { remote: "none", local: "write" };
|
|
11
14
|
const FETCH = { remote: "read", local: "write" };
|
|
15
|
+
const REMOTE_WRITE = { remote: "write", local: "write" };
|
|
12
16
|
const GROUP = { remote: "none", local: "none" };
|
|
13
17
|
/** How many pieces one `sync --embed` embeds, as drive's. */
|
|
14
18
|
const EMBED_MAX = 600;
|
|
@@ -132,6 +136,8 @@ const addCalendarCommandsDefinition = (program, deps) => {
|
|
|
132
136
|
...(opts.newest ? { newest: true } : {}),
|
|
133
137
|
}), command);
|
|
134
138
|
});
|
|
139
|
+
if (deps.writes)
|
|
140
|
+
addEventWrites(events, deps.writes, { deps, now, zone, output, print, example });
|
|
135
141
|
const people = annotate(program.command("people").description("the people in the account's events"), {
|
|
136
142
|
effects: GROUP,
|
|
137
143
|
});
|
|
@@ -172,4 +178,69 @@ const addCalendarCommandsDefinition = (program, deps) => {
|
|
|
172
178
|
print(await resolveAnswer(deps.store, review, choice, now()), command);
|
|
173
179
|
});
|
|
174
180
|
};
|
|
175
|
-
|
|
181
|
+
const collect = (value, previous = []) => [...previous, value];
|
|
182
|
+
const addEventWrites = (events, writes, { deps, now, zone, output, print, example }) => {
|
|
183
|
+
const context = (command) => ({
|
|
184
|
+
store: deps.store,
|
|
185
|
+
accountId: deps.accountId,
|
|
186
|
+
writes,
|
|
187
|
+
ask: terminalAsker(command),
|
|
188
|
+
now: now(),
|
|
189
|
+
zone: zone(),
|
|
190
|
+
names: { notify: "--notify or --no-notify", id: "--id", calendar: "--calendar", series: "--series" },
|
|
191
|
+
});
|
|
192
|
+
// Left out on purpose: with other attendees the owner must choose, so neither switch is the default.
|
|
193
|
+
const notify = (command) => command
|
|
194
|
+
.option("--notify", "the provider mails the other attendees; needs events.notify allowed")
|
|
195
|
+
.option("--no-notify", "the provider mails nobody; one of the two is required when there are other attendees");
|
|
196
|
+
const fields = (command, replacing) => command
|
|
197
|
+
.option("--title <title>", "the event's title")
|
|
198
|
+
.option("--start-time <time>", "when it starts: a time in your zone, or one with its own")
|
|
199
|
+
.option("--end-time <time>", "when it ends")
|
|
200
|
+
.option("--start-date <date>", "the first day of an all-day event, YYYY-MM-DD")
|
|
201
|
+
.option("--end-date <date>", "the last day of an all-day event")
|
|
202
|
+
.option("--location <text>", "where; empty clears it")
|
|
203
|
+
.option("--description <text>", "the details; empty clears it")
|
|
204
|
+
.option("--attendee <email>", `an attendee; repeat for each${replacing ? ", replacing the list" : ""}`, collect);
|
|
205
|
+
const fieldsOf = ({ attendee, ...given }) => ({
|
|
206
|
+
...Object.fromEntries(Object.entries(given).filter(([, value]) => value !== undefined)),
|
|
207
|
+
...(attendee === undefined ? {} : { attendees: attendee }),
|
|
208
|
+
});
|
|
209
|
+
const choicesOf = ({ calendar, series, notify }) => ({
|
|
210
|
+
...(calendar === undefined ? {} : { calendar }),
|
|
211
|
+
...(series ? { series: true } : {}),
|
|
212
|
+
...(notify === undefined ? {} : { notify }),
|
|
213
|
+
});
|
|
214
|
+
const write = (name) => annotate(events.command(name), { effects: REMOTE_WRITE, offline: false });
|
|
215
|
+
const target = (command) => command
|
|
216
|
+
.argument("<event>", "the event's id, or event:<id>")
|
|
217
|
+
.option("--calendar <id>", "the calendar, when the event is on more than one of the account's");
|
|
218
|
+
output(example(notify(fields(write("create"), false)
|
|
219
|
+
.description("add an event to a calendar; one that repeats is made in the calendar itself")
|
|
220
|
+
.argument("<calendar>", "the calendar's id, or the provider's id")).option("--id <id>", "the id a create that ended in outcome_unknown named: a retry finds that event"), "events create 3 --title 'Example planning' --start-time 2026-11-02T10:00 --end-time 2026-11-02T11:00", "events create 3 --title 'Example review' --start-date 2026-11-03 --end-date 2026-11-03 " +
|
|
221
|
+
"--attendee ana@example.com --no-notify")).action(async (calendar, opts, command) => {
|
|
222
|
+
const { notify: choice, id, ...rest } = opts;
|
|
223
|
+
print(await createEvent(context(command), calendar, fieldsOf(rest), {
|
|
224
|
+
...(choice === undefined ? {} : { notify: choice }),
|
|
225
|
+
...(id === undefined ? {} : { id }),
|
|
226
|
+
}), command);
|
|
227
|
+
});
|
|
228
|
+
output(example(notify(target(fields(write("update"), true))
|
|
229
|
+
.description("change an event; with --series every occurrence of the repeating event")
|
|
230
|
+
.option("--series", "change the whole repeating event, not only this occurrence")), "events update event:42 --start-time 2026-11-02T14:00 --end-time 2026-11-02T15:00 --notify")).action(async (reference, opts, command) => {
|
|
231
|
+
const { calendar: _calendar, series: _series, notify: _notify, ...rest } = opts;
|
|
232
|
+
print(await updateEvent(context(command), reference, fieldsOf(rest), choicesOf(opts)), command);
|
|
233
|
+
});
|
|
234
|
+
output(example(notify(target(write("delete"))
|
|
235
|
+
.description("delete an event; a whole repeating event needs events.delete.series allowed first")
|
|
236
|
+
.option("--series", "delete the whole repeating event, not only this occurrence")), "events delete event:42 --no-notify")).action(async (reference, opts, command) => {
|
|
237
|
+
print(await deleteEvent(context(command), reference, choicesOf(opts)), command);
|
|
238
|
+
});
|
|
239
|
+
output(example(notify(target(write("respond"))
|
|
240
|
+
.description("answer an invitation; the organiser is told only with --notify")
|
|
241
|
+
.addArgument(new Argument("<response>", "the answer").choices(EVENT_RESPONSES))), "events respond event:42 accepted --notify")).action(async (reference, response, opts, command) => {
|
|
242
|
+
print(await respondToEvent(context(command), reference, response, choicesOf(opts)), command);
|
|
243
|
+
});
|
|
244
|
+
};
|
|
245
|
+
// honoursDryRun: a host program other than `run` would otherwise let a write through under --dry-run.
|
|
246
|
+
export const addCalendarCommands = honoursDryRun(sharedCommand(addCalendarCommandsDefinition));
|
package/dist/calendar/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export type { CalendarAccessRole, CalendarInput, EntryAttachment, EntryAttachmentInput, EntryEventType, EntryInput, EntryKind, EntryParticipantInput, EntryRevision, EntryStatus, EntryTransparency, EntryVisibility, EventDetails, EventFilter, EventStore, LinkReview, ParticipantResponse, ReviewDecision, StoredCalendar, StoredEntry, StoredParticipant, } from "../meetings/index.js";
|
|
2
2
|
export type { CalendarExpander, CalendarSource, CalendarSourceTraits, ChangePage, ChangeResult, SyncWindow, } from "./source.js";
|
|
3
3
|
export { type CalendarCursor, type CalendarSyncOptions, type CalendarSyncReport, type CalendarSyncResult, calendarSyncWindow, parseCalendarCursor, syncCalendars, } from "./sync.js";
|
|
4
|
+
export { CALENDAR_WRITE_DEFAULTS, CALENDAR_WRITE_KEYS, type CalendarWrite, type CalendarWriter, calendarConflict, EVENTS_DELETE_SERIES_KEY, EVENTS_NOTIFY_KEY, type EventDraft, type EventPatch, type EventResponse, type WriteScope, } from "./writer.js";
|
|
5
|
+
export type { CalendarWrites } from "./writes.js";
|
package/dist/calendar/index.js
CHANGED