@wirecat/cli-core 0.538.0 → 0.540.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/calendar/busy.js +1 -1
  3. package/dist/calendar/cli/answers.d.ts +36 -0
  4. package/dist/calendar/cli/answers.js +17 -0
  5. package/dist/calendar/cli/index.d.ts +4 -0
  6. package/dist/calendar/cli/index.js +77 -12
  7. package/dist/calendar/ics.d.ts +55 -0
  8. package/dist/calendar/ics.js +314 -0
  9. package/dist/calendar/index.d.ts +1 -0
  10. package/dist/calendar/index.js +1 -0
  11. package/dist/calendar/mcp/index.js +2 -1
  12. package/dist/calendar/prepare.d.ts +2 -0
  13. package/dist/calendar/reads.d.ts +17 -4
  14. package/dist/calendar/reads.js +116 -27
  15. package/dist/calendar/testing/memory.js +5 -0
  16. package/dist/calendar/testing/writer.d.ts +1 -1
  17. package/dist/calendar/testing/writer.js +40 -0
  18. package/dist/calendar/writer.d.ts +15 -1
  19. package/dist/calendar/writer.js +2 -0
  20. package/dist/calendar/writes.d.ts +46 -0
  21. package/dist/calendar/writes.js +98 -0
  22. package/dist/cli/bot/mcp.js +2 -2
  23. package/dist/cli/effects.d.ts +14 -0
  24. package/dist/cli/effects.js +33 -2
  25. package/dist/cli/envelope.d.ts +4 -0
  26. package/dist/cli/index.d.ts +2 -2
  27. package/dist/cli/index.js +2 -2
  28. package/dist/cli/list-query.d.ts +8 -3
  29. package/dist/cli/list-query.js +21 -15
  30. package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
  31. package/dist/errors/reasons.js +7 -0
  32. package/dist/mcp/agent.js +12 -3
  33. package/dist/mcp/app-server.js +2 -2
  34. package/dist/mcp/server.js +2 -2
  35. package/dist/meetings/event-store.d.ts +6 -0
  36. package/dist/meetings/testing/event-store-contract.js +15 -0
  37. package/dist/store/sqlite/calendar-events.js +15 -0
  38. package/package.json +3 -3
@@ -8,7 +8,7 @@ import * as v from "valibot";
8
8
  import { findCommand } from "../cli/command-contract.js";
9
9
  import { environmentOf } from "../cli/context.js";
10
10
  import { refuseWriteBeforeConnecting } from "../cli/early-refusal.js";
11
- import { changesAuthorityAt, fetchesOnlyAt, judgedTargets } from "../cli/effects.js";
11
+ import { changesAuthorityAt, judgedTargets, readsOverMcpAt } from "../cli/effects.js";
12
12
  import { itemOf } from "../cli/envelope.js";
13
13
  import { DEFAULT_OUTPUT_BYTES } from "../cli/execution.js";
14
14
  import { recalledAccount } from "../cli/messenger/accounts.js";
@@ -127,7 +127,7 @@ export const createServer = (command, context, messenger, sessionOptions) => {
127
127
  readOnly: readOnlyLockOf(app, context.env) !== undefined,
128
128
  keyOf,
129
129
  authority,
130
- fetchOnly: fetchesOnlyAt(root),
130
+ fetchOnly: readsOverMcpAt(root),
131
131
  offline: true,
132
132
  stdin,
133
133
  }).map((one) => "sync_first" in one.input.entries && !syncAllowed(one.path)
@@ -204,7 +204,13 @@ export interface EventFilter {
204
204
  accountId?: number;
205
205
  /** Several accounts' events at once, in place of `accountId`. */
206
206
  accountIds?: readonly number[];
207
+ /** Only this event. */
208
+ id?: number;
207
209
  calendarId?: number;
210
+ /** Events with a copy, in the accounts read, of this kind; a copy stored with no kind is `default`. */
211
+ eventType?: EntryEventType;
212
+ /** Events with no copy, in the accounts read, of any of these kinds. */
213
+ notEventTypes?: readonly EntryEventType[];
208
214
  /** Events whose entries list one of this person's contacts. */
209
215
  personId?: number;
210
216
  /** Latest start first; earliest first when absent. */
@@ -383,6 +383,21 @@ export const eventStoreContract = (make) => {
383
383
  assert.equal((await store.events({ ...window, accountIds: [fixture.accountId, fixture.otherAccountId] })).length, 3);
384
384
  assert.equal((await store.events({ ...window, accountIds: [fixture.otherAccountId] })).length, 1);
385
385
  }),
386
+ test("events filter by the kind of their copies; a copy with no kind is default", async ({ store, calendar }) => {
387
+ const at = later;
388
+ await store.saveEntries(calendar, [
389
+ single("meeting", at, { title: "meeting" }),
390
+ single("plain", at + HOUR, { title: "plain", eventType: "default" }),
391
+ single("cake", at + 2 * HOUR, { title: "cake", eventType: "birthday" }),
392
+ single("deep", at + 3 * HOUR, { title: "deep", eventType: "focus" }),
393
+ ], 2);
394
+ const window = { from: at - HOUR, to: at + 8 * HOUR };
395
+ const titles = async (filter) => (await store.events({ ...window, ...filter })).map(({ title }) => title);
396
+ assert.deepEqual(await titles({ eventType: "default" }), ["meeting", "plain"]);
397
+ assert.deepEqual(await titles({ eventType: "birthday" }), ["cake"]);
398
+ assert.deepEqual(await titles({ notEventTypes: ["birthday", "focus"] }), ["meeting", "plain"]);
399
+ assert.deepEqual(await titles({ eventType: "default", notEventTypes: ["default"] }), []);
400
+ }),
386
401
  test("participants are kept by address; a room is listed and is no contact", async ({ store, calendar }) => {
387
402
  const [entry] = await store.saveEntries(calendar, [
388
403
  single("a", later, {
@@ -43,6 +43,10 @@ export const listEvents = (database, filter) => {
43
43
  clauses.push("e.starts_at >= ?");
44
44
  params.push(filter.startsFrom - DAY);
45
45
  }
46
+ if (filter.id !== undefined) {
47
+ clauses.push("e.id = ?");
48
+ params.push(filter.id);
49
+ }
46
50
  if (filter.calendarId !== undefined) {
47
51
  clauses.push(entries("ce.calendar_id = ?"));
48
52
  params.push(filter.calendarId);
@@ -57,6 +61,17 @@ export const listEvents = (database, filter) => {
57
61
  };
58
62
  if (accounts !== undefined)
59
63
  clauses.push(entries(inAccounts("c.account_id")));
64
+ // A copy's kind counts only in the accounts read: another account's copy of the same meeting is its own.
65
+ const ofKind = (kinds) => {
66
+ for (const kind of kinds)
67
+ params.push(kind);
68
+ const scope = accounts === undefined ? "" : ` AND ${inAccounts("c.account_id")}`;
69
+ return entries(`coalesce(ce.event_type, 'default') IN (${kinds.map(() => "?").join(", ")})${scope}`);
70
+ };
71
+ if (filter.eventType !== undefined)
72
+ clauses.push(ofKind([filter.eventType]));
73
+ if (filter.notEventTypes?.length)
74
+ clauses.push(`NOT ${ofKind(filter.notEventTypes)}`);
60
75
  if (filter.personId !== undefined) {
61
76
  clauses.push(entries("EXISTS (SELECT 1 FROM calendar_entry_participants p JOIN identity_links il ON il.identity_id = p.identity_id " +
62
77
  "WHERE p.entry_id = ce.id AND il.person_id = ?)"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-core",
3
- "version": "0.538.0",
3
+ "version": "0.540.0",
4
4
  "description": "The messenger-neutral half of a messaging CLI: domain model, message locators, rendering, name resolution, the SQLite seam, the command skeleton and shared read commands, the send guard, run records and the message store",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Viacheslav Ptsarev",
@@ -251,7 +251,7 @@
251
251
  "@types/node": "^22.20.5",
252
252
  "@types/pngjs": "6.0.5",
253
253
  "@vitest/coverage-v8": "^5.0.5",
254
- "@wirecat/cli-base": "0.71.0",
254
+ "@wirecat/cli-base": "0.72.0",
255
255
  "@wirecat/cli-core-legacy-tags": "npm:@wirecat/cli-core@0.414.0",
256
256
  "commander": "15.0.0",
257
257
  "cspell": "10.4.0",
@@ -271,7 +271,7 @@
271
271
  },
272
272
  "peerDependencies": {
273
273
  "@napi-rs/canvas": ">=0.1.69 <2",
274
- "@wirecat/cli-base": ">=0.71.0 <0.72.0",
274
+ "@wirecat/cli-base": ">=0.72.0 <0.73.0",
275
275
  "commander": ">=15"
276
276
  },
277
277
  "bin": {