@wirecat/cli-core 0.409.0 → 0.411.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 (70) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/dist/cli/bot/command.js +1 -0
  3. package/dist/cli/bot/port.d.ts +1 -1
  4. package/dist/cli/bot/setup.js +11 -5
  5. package/dist/cli/commands-command.js +18 -12
  6. package/dist/cli/contract-evidence.d.ts +26 -0
  7. package/dist/cli/contract-evidence.js +58 -0
  8. package/dist/cli/contract-templates.d.ts +4 -0
  9. package/dist/cli/contract-templates.js +83 -0
  10. package/dist/cli/discovery-descriptions.d.ts +4 -0
  11. package/dist/cli/discovery-descriptions.js +4 -0
  12. package/dist/cli/index.d.ts +22 -55
  13. package/dist/cli/index.js +19 -55
  14. package/dist/cli/messenger/chat-answers.d.ts +1 -1
  15. package/dist/cli/messenger/chat-answers.js +1 -1
  16. package/dist/cli/messenger/chat-lists.d.ts +1 -1
  17. package/dist/cli/messenger/chats-command.js +3 -0
  18. package/dist/cli/messenger/contacts-command.js +23 -15
  19. package/dist/cli/messenger/guard-commands.d.ts +5 -1
  20. package/dist/cli/messenger/guard-commands.js +5 -3
  21. package/dist/cli/messenger/messages-press-command.js +4 -1
  22. package/dist/cli/messenger/models-command.js +3 -11
  23. package/dist/cli/messenger/records-command.js +1 -1
  24. package/dist/cli/messenger/report.d.ts +21 -6
  25. package/dist/cli/messenger/report.js +20 -5
  26. package/dist/cli/messenger/tasks-command.js +14 -4
  27. package/dist/cli/model-catalogue-command.d.ts +15 -0
  28. package/dist/cli/model-catalogue-command.js +110 -0
  29. package/dist/cli/program.js +2 -0
  30. package/dist/cli/runs/search-command.js +2 -3
  31. package/dist/cli/runs/search-query.d.ts +1 -1
  32. package/dist/cli/runs/search-query.js +18 -2
  33. package/dist/cli/runs/search.d.ts +2 -1
  34. package/dist/cli/runs/search.js +4 -3
  35. package/dist/cli/shared-command-exports.d.ts +644 -0
  36. package/dist/cli/shared-command-exports.js +114 -0
  37. package/dist/cli/shared-contracts.d.ts +1 -0
  38. package/dist/cli/shared-contracts.js +1 -0
  39. package/dist/domain/attachments.js +1 -0
  40. package/dist/entities/chat.d.ts +3 -3
  41. package/dist/entities/chat.js +2 -2
  42. package/dist/entities/contact-context.d.ts +8 -8
  43. package/dist/entities/contact.d.ts +4 -4
  44. package/dist/entities/index.d.ts +11 -12
  45. package/dist/entities/list-results.d.ts +1 -1
  46. package/dist/entities/list-results.js +1 -1
  47. package/dist/entities/send-attempt.d.ts +0 -1
  48. package/dist/entities/send-attempt.js +0 -1
  49. package/dist/errors/reasons.d.ts +1 -1
  50. package/dist/errors/reasons.js +7 -0
  51. package/dist/meetings/cli/index.d.ts +1 -1
  52. package/dist/meetings/cli/index.js +25 -9
  53. package/dist/meetings/cli/passage.d.ts +0 -1
  54. package/dist/meetings/cli/passage.js +5 -3
  55. package/dist/meetings/cli/project-evidence.js +3 -2
  56. package/dist/meetings/cli/search.d.ts +2 -2
  57. package/dist/meetings/cli/search.js +14 -12
  58. package/dist/meetings/event-query.d.ts +1 -0
  59. package/dist/meetings/event-query.js +15 -0
  60. package/dist/meetings/index.d.ts +1 -0
  61. package/dist/meetings/index.js +1 -0
  62. package/dist/meetings/store.d.ts +2 -0
  63. package/dist/meetings/testing/memory.js +5 -0
  64. package/dist/services/tasks.d.ts +2 -0
  65. package/dist/services/tasks.js +6 -4
  66. package/dist/store/sqlite/events.d.ts +2 -1
  67. package/dist/store/sqlite/events.js +16 -1
  68. package/dist/store/sqlite/meetings.js +3 -0
  69. package/package.json +3 -3
  70. package/parity.json +61 -76
@@ -12,6 +12,7 @@ export interface MeetingSearchFlags {
12
12
  project?: string;
13
13
  tag?: string;
14
14
  topic?: string;
15
+ topicName?: string;
15
16
  sinceTime?: number;
16
17
  untilTime?: number;
17
18
  /** The zone a date or time without one was read in; the parser sets it, since the typed text is gone after. */
@@ -20,14 +21,13 @@ export interface MeetingSearchFlags {
20
21
  cursor?: string;
21
22
  maxReadBytes?: number;
22
23
  }
23
- /** Shared grammar without an action: a host with other source reports supplies their transport routing. */
24
- export declare const createMeetingSearchCommand: (program: Command) => Command;
25
24
  /** Transport fields become one validated word request before a host opens its selected source. */
26
25
  export declare const meetingHitSearchRequest: (query: string, options: MeetingSearchFlags, signal?: AbortSignal) => MeetingHitSearchOptions;
27
26
  /** Parsed dates must become ISO values again so the CLI can replay its own continuation. */
28
27
  export declare const meetingSearchArguments: (query: string, options: MeetingSearchFlags) => Record<string, unknown>;
29
28
  /** What `meta` says about the dates: the zone one without its own was read in, and the window in UTC. */
30
29
  export declare const meetingSearchMeta: (options: MeetingSearchFlags) => Pick<Meta, "timezone" | "window">;
30
+ export declare const createMeetingSearchCommand: (program: Command) => Command;
31
31
  export declare const addMeetingSearchCommand: (program: Command, deps: {
32
32
  search: (query: string, request: MeetingHitSearchOptions, options: MeetingSearchFlags & Record<string, unknown>) => Promise<MeetingSearchPage>;
33
33
  signal: () => AbortSignal | undefined;
@@ -1,5 +1,5 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
- import { annotate } from "@wirecat/cli-base/commands";
2
+ import { annotate, sharedCommand } from "@wirecat/cli-base/commands";
3
3
  import { timezoneOf, zonedMoment } from "@wirecat/cli-base/query";
4
4
  import { Command } from "commander";
5
5
  import { meetingHitsList } from "../answers.js";
@@ -25,7 +25,7 @@ const integer = (value) => {
25
25
  return Number(value);
26
26
  };
27
27
  /** Shared grammar without an action: a host with other source reports supplies their transport routing. */
28
- export const createMeetingSearchCommand = (program) => {
28
+ const createMeetingSearchCommandDefinition = (program) => {
29
29
  let search = program.commands.find((command) => command.name() === "search");
30
30
  if (!search) {
31
31
  search = new Command("search").description("search the stored resources");
@@ -41,9 +41,9 @@ export const createMeetingSearchCommand = (program) => {
41
41
  .option("--person-id <id>", "meetings with a participant linked to this person", integer)
42
42
  .option("--event-id <id>", "only meetings linked to this stored event", integer)
43
43
  .option("--series-id <id>", "only meetings in this stored series", integer)
44
- .option("--project <name>", "meetings confirmed in this project, by name, key or alias")
44
+ .option("--project <project>", "meetings confirmed in this project, by name, key or alias")
45
45
  .option("--tag <name>", "meetings with this tag, by name or alias")
46
- .option("--topic <name>", "meetings with this topic, by name or alias")
46
+ .option("--topic-name <name>", "meetings with this topic, by name or alias")
47
47
  .option("--since-time <time>", "meetings starting at or after this ISO 8601 date or time", moment("--since-time", false, readIn))
48
48
  .option("--until-time <time>", "meetings starting at or before this ISO 8601 date or time; a date alone covers that whole local day", moment("--until-time", true, readIn))
49
49
  .option("--limit <n>", "how many hits, 1 through 1000 (default 100)", integer)
@@ -66,7 +66,7 @@ export const meetingHitSearchRequest = (query, options, signal) => {
66
66
  meetingSeriesId: options.seriesId,
67
67
  project: options.project,
68
68
  tag: options.tag,
69
- topic: options.topic,
69
+ topic: options.topicName ?? options.topic,
70
70
  since: options.sinceTime,
71
71
  until: options.untilTime,
72
72
  limit: options.limit,
@@ -81,14 +81,14 @@ export const meetingHitSearchRequest = (query, options, signal) => {
81
81
  /** Parsed dates must become ISO values again so the CLI can replay its own continuation. */
82
82
  export const meetingSearchArguments = (query, options) => Object.fromEntries(Object.entries({
83
83
  query,
84
- meeting_id: options.meetingId,
85
- identity_id: options.identityId,
86
- person_id: options.personId,
87
- event_id: options.eventId,
88
- series_id: options.seriesId,
84
+ meeting_id: options.meetingId?.toString(),
85
+ identity_id: options.identityId?.toString(),
86
+ person_id: options.personId?.toString(),
87
+ event_id: options.eventId?.toString(),
88
+ series_id: options.seriesId?.toString(),
89
89
  project: options.project,
90
90
  tag: options.tag,
91
- topic: options.topic,
91
+ topic_name: options.topicName ?? options.topic,
92
92
  since_time: options.sinceTime === undefined ? undefined : new Date(options.sinceTime).toISOString(),
93
93
  until_time: options.untilTime === undefined ? undefined : new Date(options.untilTime).toISOString(),
94
94
  max_read_bytes: options.maxReadBytes ?? 4194304,
@@ -104,7 +104,9 @@ export const meetingSearchMeta = (options) => {
104
104
  ...(Object.keys(window).length === 0 ? {} : { window }),
105
105
  };
106
106
  };
107
- export const addMeetingSearchCommand = (program, deps) => createMeetingSearchCommand(program).action(async (query, options) => {
107
+ const addMeetingSearchCommandDefinition = (program, deps) => createMeetingSearchCommand(program).action(async (query, options) => {
108
108
  const request = meetingHitSearchRequest(query, options, deps.signal());
109
109
  deps.output(meetingHitsList(await deps.search(query, request, options), request.limit ?? 100, "search meetings", meetingSearchArguments(query, options), meetingSearchMeta(options)));
110
110
  });
111
+ export const createMeetingSearchCommand = sharedCommand(createMeetingSearchCommandDefinition);
112
+ export const addMeetingSearchCommand = sharedCommand(addMeetingSearchCommandDefinition);
@@ -0,0 +1 @@
1
+ export declare const EVENT_QUERY: import("@wirecat/cli-base/query").QueryRegistry;
@@ -0,0 +1,15 @@
1
+ import { defineQuery } from "@wirecat/cli-base/query";
2
+ export const EVENT_QUERY = defineQuery({
3
+ entity: "event",
4
+ backend: "sql",
5
+ fields: [
6
+ { name: "id", type: "num", operators: ["term", "range"], example: "id:1" },
7
+ { name: "origin", type: "enum", operators: ["term"], values: ["auto", "owner"], example: "origin:owner" },
8
+ ...["starts_at", "ends_at", "created_at", "updated_at"].map((name) => ({
9
+ name,
10
+ type: "time",
11
+ operators: ["term", "range"],
12
+ example: `${name}>=2026-01-01`,
13
+ })),
14
+ ],
15
+ });
@@ -2,6 +2,7 @@ export { MEETING_STEPS, type MeetingDetailsItem, type MeetingHitItem, type Meeti
2
2
  export type { MeetingBatchReader } from "./batch.js";
3
3
  export type { MeetingContextStore, MeetingEventLinkInput, MeetingEventLinkReceipt, ParticipantPerson, ParticipantPersonInput, ParticipantPersonLinkInput, ParticipantPersonLinkReceipt, } from "./context-store.js";
4
4
  export { type MeetingCueIdentity, meetingCueFingerprint } from "./cue-fingerprint.js";
5
+ export { EVENT_QUERY } from "./event-query.js";
5
6
  export { linkEvent } from "./events.js";
6
7
  export { exportMeeting, type MeetingExport, type MeetingExportFormat, type MeetingExportOptions, serializeVtt, } from "./export.js";
7
8
  export { type FindingEvidence, type FindingOutcome, findingHash, MEETING_FINDINGS_KIND, type MeetingFactFinding, type MeetingFinding, type MeetingFindingsInput, type MeetingFindingsReceipt, type MeetingTaskFinding, meetingFindingFingerprint, validateMeetingFindings, } from "./findings.js";
@@ -1,5 +1,6 @@
1
1
  export { MEETING_STEPS, meetingDetails, meetingHit, meetingHitsList, meetingSummary, meetingTranscript, } from "./answers.js";
2
2
  export { meetingCueFingerprint } from "./cue-fingerprint.js";
3
+ export { EVENT_QUERY } from "./event-query.js";
3
4
  export { linkEvent } from "./events.js";
4
5
  export { exportMeeting, serializeVtt, } from "./export.js";
5
6
  export { findingHash, MEETING_FINDINGS_KIND, meetingFindingFingerprint, validateMeetingFindings, } from "./findings.js";
@@ -111,6 +111,8 @@ export interface MeetingStore {
111
111
  participants(query: string, accountId?: number): Promise<Participant[]>;
112
112
  search(query: string, filter?: MeetingFilter): Promise<SearchHit[]>;
113
113
  events(): Promise<Event[]>;
114
+ /** Filters at the store; adapters without this method refuse nonempty event queries. */
115
+ queryEvents?(query: QueryAst): Promise<Event[]>;
114
116
  /** Inclusive overlap; an event with a start but no end occupies its start instant. */
115
117
  eventCandidates(filter: EventCandidateFilter): Promise<Event[]>;
116
118
  createEvent(input: NewRecord<Event>, now: number): Promise<Event>;
@@ -1,5 +1,7 @@
1
1
  import { evaluate } from "@wirecat/cli-base/query";
2
+ import { EVENT_QUERY } from "../event-query.js";
2
3
  import { MEETING_QUERY, meetingItem } from "../item.js";
4
+ import { eventItem } from "../read-answers.js";
3
5
  import { MeetingError, positiveInteger } from "../reads.js";
4
6
  const overlap = (start, end, from, to) => start !== null && start <= to && (end ?? start) >= from;
5
7
  /** A test store keeps no accounts: every id is an account of the `memory` provider. */
@@ -136,6 +138,9 @@ export const memoryMeetingStore = () => {
136
138
  async events() {
137
139
  return structuredClone(state.events.filter((e) => e.deletedAt === null));
138
140
  },
141
+ async queryEvents(query) {
142
+ return structuredClone(state.events.filter((event) => event.deletedAt === null && evaluate(query, EVENT_QUERY.fields, { ...eventItem(event), id: event.id })));
143
+ },
139
144
  async eventCandidates(filter) {
140
145
  const series = state.series.find((s) => s.id === filter.meetingSeriesId);
141
146
  return structuredClone(state.events.filter((e) => e.deletedAt === null &&
@@ -60,6 +60,8 @@ export interface TaskList {
60
60
  * from a message: the message stays a link, so deleting it leaves no copy here.
61
61
  */
62
62
  export interface TaskPlan {
63
+ priority?: number | null;
64
+ parent?: string | null;
63
65
  title?: string;
64
66
  description?: string | null;
65
67
  dueAt?: Date | null;
@@ -170,7 +170,7 @@ export const tasksService = (deps, categories) => {
170
170
  const planOf = async (store, id, plan, by) => {
171
171
  if (plan.category !== undefined)
172
172
  plan = { ...plan, category: await categoryOf(store, plan.category) };
173
- const open = (await store.tasks.get(id))?.state === "open";
173
+ const open = (await store.tasks.details(id)).status === "open";
174
174
  if (!open && (plan.dueAt != null || plan.nextCheckAt != null))
175
175
  throw new CliError("validation_error", "a closed task takes no due date or next check; it stays closed");
176
176
  const { assignees, ...fields } = plan;
@@ -405,10 +405,12 @@ export const tasksService = (deps, categories) => {
405
405
  await store.tasks.suggestClose(id, null, "owner");
406
406
  return cardOf(store, id);
407
407
  }),
408
- plan: (id, plan, by) => inStore(async (store, account) => {
409
- await owned(store, account, id);
408
+ plan: async (id, plan, by) => {
409
+ const store = await deps.store();
410
+ if (!(await store.tasks.projectTask(id)))
411
+ await owned(store, await deps.account(), id);
410
412
  return planOf(store, id, plan, by);
411
- }),
413
+ },
412
414
  link: async (id, { references = [], chats = [] }, { kind, by }) => {
413
415
  const written = references.map((one) => one.trim()).filter(Boolean);
414
416
  const relationKind = TASK_RELATION_KINDS.includes(kind);
@@ -1,3 +1,4 @@
1
+ import type { QueryAst } from "@wirecat/cli-base/query";
1
2
  import type { Event, EventCandidateFilter, EventSeries, NewRecord } from "../../meetings/index.js";
2
3
  import type { CacheDatabase } from "../driver.js";
3
4
  /** JSON columns keep a missing value as SQL NULL, never the text `null`, so a read gives back what was saved. */
@@ -12,7 +13,7 @@ export declare const page: (filter: {
12
13
  limit?: number;
13
14
  offset?: number;
14
15
  }) => [number, number];
15
- export declare const events: (database: CacheDatabase) => Event[];
16
+ export declare const events: (database: CacheDatabase, query?: QueryAst) => Event[];
16
17
  /** An event without an end occupies its start instant. */
17
18
  export declare const eventCandidates: (database: CacheDatabase, filter: EventCandidateFilter) => Event[];
18
19
  export declare const createEvent: (database: CacheDatabase, input: NewRecord<Event>, now: number) => Event;
@@ -1,3 +1,4 @@
1
+ import { queryFilter } from "./query-filter.js";
1
2
  /** JSON columns keep a missing value as SQL NULL, never the text `null`, so a read gives back what was saved. */
2
3
  export const toJson = (value) => value === null || value === undefined ? null : JSON.stringify(value);
3
4
  export const fromJson = (value) => (value === null || value === undefined ? null : JSON.parse(String(value)));
@@ -36,7 +37,21 @@ const seriesOf = (row) => ({
36
37
  updatedAt: Number(row.updated_at),
37
38
  });
38
39
  const exists = (database, table, id) => database.prepare(`SELECT 1 FROM ${table} WHERE id = ?`).get(id) !== undefined;
39
- export const events = (database) => database.prepare("SELECT * FROM events WHERE deleted_at IS NULL ORDER BY id").all().map(eventOf);
40
+ export const events = (database, query) => {
41
+ const where = query &&
42
+ queryFilter(query, {
43
+ id: { type: "num", sql: "id" },
44
+ origin: { type: "enum", sql: "origin" },
45
+ starts_at: { type: "time", sql: "starts_at" },
46
+ ends_at: { type: "time", sql: "ends_at" },
47
+ created_at: { type: "time", sql: "created_at" },
48
+ updated_at: { type: "time", sql: "updated_at" },
49
+ });
50
+ return database
51
+ .prepare(`SELECT * FROM events WHERE deleted_at IS NULL${where ? ` AND (${where.sql})` : ""} ORDER BY id`)
52
+ .all(...(where?.params ?? []))
53
+ .map(eventOf);
54
+ };
40
55
  /** An event without an end occupies its start instant. */
41
56
  export const eventCandidates = (database, filter) => database
42
57
  .prepare(`SELECT e.* FROM events e
@@ -620,6 +620,9 @@ export const meetingStoreOver = (context) => {
620
620
  async events() {
621
621
  return eventQueries.events(database);
622
622
  },
623
+ async queryEvents(query) {
624
+ return eventQueries.events(database, query);
625
+ },
623
626
  async eventCandidates(filter) {
624
627
  return eventQueries.eventCandidates(database, filter);
625
628
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-core",
3
- "version": "0.409.0",
3
+ "version": "0.411.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",
@@ -209,7 +209,7 @@
209
209
  "@types/node": "^22.20.4",
210
210
  "@types/pngjs": "6.0.5",
211
211
  "@vitest/coverage-v8": "^5.0.2",
212
- "@wirecat/cli-base": "0.50.2",
212
+ "@wirecat/cli-base": "0.51.0",
213
213
  "commander": "15.0.0",
214
214
  "cspell": "10.3.6",
215
215
  "drizzle-kit": "1.0.0-rc.4",
@@ -229,7 +229,7 @@
229
229
  },
230
230
  "peerDependencies": {
231
231
  "@napi-rs/canvas": ">=0.1.69 <2",
232
- "@wirecat/cli-base": ">=0.50.2 <0.51.0",
232
+ "@wirecat/cli-base": ">=0.51.0 <0.52.0",
233
233
  "commander": ">=15"
234
234
  },
235
235
  "bin": {