@wirecat/cli-core 0.593.0 → 0.595.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 (52) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/background/schedules.d.ts +10 -1
  3. package/dist/background/schedules.js +42 -5
  4. package/dist/cli/index.d.ts +1 -1
  5. package/dist/cli/messenger/store-maintenance-command.js +54 -5
  6. package/dist/cli/schedule/schedule-command.d.ts +4 -2
  7. package/dist/cli/schedule/schedule-command.js +3 -1
  8. package/dist/crm/cli/index.d.ts +6 -0
  9. package/dist/crm/cli/index.js +118 -2
  10. package/dist/crm/index.d.ts +2 -0
  11. package/dist/crm/index.js +2 -0
  12. package/dist/crm/testing/index.d.ts +2 -1
  13. package/dist/crm/testing/index.js +2 -1
  14. package/dist/crm/testing/source-contract.js +2 -2
  15. package/dist/crm/testing/source.d.ts +22 -2
  16. package/dist/crm/testing/source.js +117 -1
  17. package/dist/crm/testing/sync-contract.js +3 -2
  18. package/dist/crm/testing/write-contract.d.ts +21 -0
  19. package/dist/crm/testing/write-contract.js +127 -0
  20. package/dist/crm/writer.d.ts +76 -0
  21. package/dist/crm/writer.js +14 -0
  22. package/dist/crm/writes.d.ts +68 -0
  23. package/dist/crm/writes.js +245 -0
  24. package/dist/domain/crm.d.ts +3 -4
  25. package/dist/entities/index.d.ts +14 -2
  26. package/dist/entities/store-maintenance.d.ts +14 -2
  27. package/dist/entities/store-maintenance.js +19 -6
  28. package/dist/errors/reasons.js +2 -1
  29. package/dist/mail/cli/review.js +11 -4
  30. package/dist/mail/marks.d.ts +2 -0
  31. package/dist/mail/marks.js +8 -6
  32. package/dist/mail/newsletter.d.ts +10 -0
  33. package/dist/mail/newsletter.js +70 -0
  34. package/dist/mail/review.d.ts +1 -0
  35. package/dist/store/migrations.d.ts +11 -2
  36. package/dist/store/migrations.js +32 -2
  37. package/dist/store/sqlite/crm.d.ts +3 -1
  38. package/dist/store/sqlite/crm.js +37 -20
  39. package/dist/store/sqlite/deals.js +6 -4
  40. package/dist/store/sqlite/email-activity.d.ts +4 -0
  41. package/dist/store/sqlite/email-activity.js +5 -1
  42. package/dist/store/sqlite/manifest.d.ts +2 -0
  43. package/dist/store/sqlite/manifest.js +58 -5
  44. package/dist/store/sqlite/migrations.generated.js +9 -0
  45. package/dist/store/sqlite/open-store.js +8 -1
  46. package/dist/store/sqlite/rollback-copy.d.ts +49 -0
  47. package/dist/store/sqlite/rollback-copy.js +290 -0
  48. package/dist/store/sqlite/schema.d.ts +32 -0
  49. package/dist/store/sqlite/schema.js +4 -2
  50. package/dist/store/store.d.ts +1 -0
  51. package/dist/store/store.js +1 -0
  52. package/package.json +2 -2
@@ -0,0 +1,290 @@
1
+ import { mkdirSync, rmSync, statfsSync, writeFileSync } from "node:fs";
2
+ import { basename, dirname, join, parse } from "node:path";
3
+ import { CliError } from "@wirecat/cli-base";
4
+ /**
5
+ * A breaking migration (one that raises `min_compatible`) locks older tools out of the store. Before it runs, the
6
+ * base tables it names are copied into a small file in `backups/` beside the store, with the whole schema as it was;
7
+ * `store restore <copy>` puts them back and returns the file to the old version. Derived tables are never copied:
8
+ * search indexes, their queues, vectors, chunks, mentions and involvements are rebuilt from the base tables.
9
+ */
10
+ const DERIVED = /(_fts|_words|_stems|_trigrams|_pending)$|^(embeddings|chunks|chunk_messages|mentions|involvements|search_terms)$|^vector_index/;
11
+ export const isDerivedTable = (name) => DERIVED.test(name);
12
+ /**
13
+ * Always copied, a few rows each: migrations seed rows into them, and the next upgrade after a rollback would insert
14
+ * those rows again and fail on its own unique keys.
15
+ */
16
+ const BOOKKEEPING = ["store_settings", "search_index_state"];
17
+ const KEY = "rollback_copy:";
18
+ const SCHEMA = "rollback_copy";
19
+ const ROWID = "__rollback_rowid";
20
+ const DAY_MS = 24 * 60 * 60 * 1000;
21
+ // The migration's own write-ahead log needs room too.
22
+ const HEADROOM = 256 * 1024 * 1024;
23
+ const quote = (name) => `"${name.replaceAll('"', '""')}"`;
24
+ const megabytes = (bytes) => `${Math.ceil(bytes / 1024 / 1024)} MB`;
25
+ const freeBytesOf = (dir) => {
26
+ const { bavail, bsize } = statfsSync(dir);
27
+ return Number(bavail) * Number(bsize);
28
+ };
29
+ const tableIn = (database, schema, name) => database.prepare(`SELECT 1 FROM ${schema}.sqlite_master WHERE type = 'table' AND name = ?`).get(name) !== undefined;
30
+ const keepsRowid = (database, schema, table) => Number(database.prepare("SELECT wr FROM pragma_table_list WHERE schema = ? AND name = ?").get(schema, table)?.wr) ===
31
+ 0;
32
+ const columnsOf = (database, schema, table) => database
33
+ .prepare("SELECT name FROM pragma_table_info(?, ?)")
34
+ .all(table, schema)
35
+ .map((row) => String(row.name));
36
+ const versionOf = (database) => Number(database.prepare("SELECT max(version) AS version FROM main.schema_migrations").get()?.version ?? 0);
37
+ const tableBytes = (database, tables) => {
38
+ try {
39
+ return Number(database
40
+ .prepare(`SELECT coalesce(sum(pgsize), 0) AS bytes FROM dbstat
41
+ WHERE schema = 'main' AND aggregate = 1 AND name IN (${tables.map(() => "?").join(", ")})`)
42
+ .get(...tables)?.bytes ?? 0);
43
+ }
44
+ catch {
45
+ // Bun's SQLite is built without dbstat: the whole file is the bound.
46
+ const pragma = (name) => Number(database.prepare(`PRAGMA ${name}`).get()?.[name] ?? 0);
47
+ return pragma("page_count") * pragma("page_size");
48
+ }
49
+ };
50
+ const stampOf = (at) => new Date(at).toISOString().replace(/[:.]/g, "-");
51
+ const attach = (database, path) => database.prepare(`ATTACH DATABASE ? AS ${SCHEMA}`).run(path);
52
+ /**
53
+ * Outside the migration's transaction (SQLite cannot attach inside one), committed on its own, so a migration that
54
+ * fails leaves it whole. Never blocks the migration: no room, or any failure, is a warning and no copy.
55
+ */
56
+ export const takeRollbackCopy = (database, { from, to, tables, at }, { store, freeBytes = freeBytesOf, warn = (line) => process.stderr.write(`${line}\n`) }) => {
57
+ const dir = join(dirname(store), "backups");
58
+ const present = [...new Set([...BOOKKEEPING, ...tables])].filter((table) => tableIn(database, "main", table));
59
+ const afterwards = `going back to schema ${from} then needs an empty store, and its data pulled again from the sources`;
60
+ let path;
61
+ try {
62
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
63
+ const needed = tableBytes(database, present);
64
+ const free = freeBytes(dir);
65
+ if (free < needed + HEADROOM) {
66
+ warn(`not enough free disk space for a rollback copy before schema ${to} (about ${megabytes(needed + HEADROOM)} ` +
67
+ `needed, ${megabytes(free)} free in ${dir}) — migrating without one; ${afterwards}`);
68
+ return undefined;
69
+ }
70
+ path = join(dir, `${parse(store).name}-rollback-schema-${from}-${stampOf(at)}.db`);
71
+ writeFileSync(path, "", { flag: "wx", mode: 0o600 });
72
+ attach(database, path);
73
+ try {
74
+ database.exec("BEGIN IMMEDIATE");
75
+ try {
76
+ const copied = versionOf(database);
77
+ database.exec(`CREATE TABLE ${SCHEMA}.rollback_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)`);
78
+ database.exec(`CREATE TABLE ${SCHEMA}.rollback_schema AS
79
+ SELECT m.type, m.name, m.tbl_name, m.sql, coalesce(l.type, m.type) AS kind
80
+ FROM main.sqlite_master m LEFT JOIN pragma_table_list l ON l.schema = 'main' AND l.name = m.name
81
+ ORDER BY m.rowid`);
82
+ for (const table of present) {
83
+ const rowid = keepsRowid(database, "main", table) ? `rowid AS ${ROWID}, ` : "";
84
+ database.exec(`CREATE TABLE ${SCHEMA}.${quote(table)} AS SELECT ${rowid}* FROM main.${quote(table)}`);
85
+ }
86
+ const meta = { from: copied, to, tables: present, created_at: at };
87
+ const insert = database.prepare(`INSERT INTO ${SCHEMA}.rollback_meta (key, value) VALUES (?, ?)`);
88
+ for (const [key, value] of Object.entries(meta))
89
+ insert.run(key, JSON.stringify(value));
90
+ database.exec("COMMIT");
91
+ return { path, from: copied, to };
92
+ }
93
+ catch (error) {
94
+ database.exec("ROLLBACK");
95
+ throw error;
96
+ }
97
+ }
98
+ finally {
99
+ database.exec(`DETACH DATABASE ${SCHEMA}`);
100
+ }
101
+ }
102
+ catch (error) {
103
+ if (path)
104
+ rmSync(path, { force: true });
105
+ warn(`the rollback copy before schema ${to} could not be written (${error instanceof Error ? error.message : String(error)}) — migrating without one; ${afterwards}`);
106
+ return undefined;
107
+ }
108
+ };
109
+ /** Inside the migration's transaction: a migration that fails never records its copy, so cleanup never takes it. */
110
+ export const recordRollbackCopy = (database, copy, at) => {
111
+ database
112
+ .prepare("INSERT OR REPLACE INTO store_settings (key, value, at) VALUES (?, ?, ?)")
113
+ .run(`${KEY}${basename(copy.path)}`, JSON.stringify({ path: copy.path, from: copy.from, to: copy.to, at }), at);
114
+ };
115
+ export const discardRollbackCopy = (copy) => rmSync(copy.path, { force: true });
116
+ /**
117
+ * The day after: a copy recorded by a migration that committed, once the store is still at that version. Only the
118
+ * recorded file is deleted, never anything else in `backups/`.
119
+ */
120
+ export const pruneRollbackCopies = (database, now) => {
121
+ const recorded = database
122
+ .prepare("SELECT key, value FROM store_settings WHERE key LIKE 'rollback\\_copy:%' ESCAPE '\\'")
123
+ .all();
124
+ if (recorded.length === 0)
125
+ return;
126
+ const version = versionOf(database);
127
+ for (const { key, value } of recorded) {
128
+ let copy;
129
+ try {
130
+ copy = JSON.parse(String(value));
131
+ }
132
+ catch {
133
+ continue;
134
+ }
135
+ if (typeof copy.path !== "string" || now - Number(copy.at) < DAY_MS || version < Number(copy.to))
136
+ continue;
137
+ if (!database.prepare("SELECT 1 FROM schema_migrations WHERE version = ?").get(Number(copy.to)))
138
+ continue;
139
+ rmSync(copy.path, { force: true });
140
+ database.prepare("DELETE FROM store_settings WHERE key = ?").run(String(key));
141
+ }
142
+ };
143
+ const objectsOf = (database, sql) => database
144
+ .prepare(sql)
145
+ .all()
146
+ .map((row) => ({
147
+ type: String(row.type),
148
+ name: String(row.name),
149
+ sql: row.sql === null ? null : String(row.sql),
150
+ kind: String(row.kind),
151
+ }));
152
+ const currentObjects = (database) => objectsOf(database, `SELECT m.type, m.name, m.sql, coalesce(l.type, m.type) AS kind
153
+ FROM main.sqlite_master m LEFT JOIN pragma_table_list l ON l.schema = 'main' AND l.name = m.name`);
154
+ const internal = ({ name, kind }) => name.startsWith("sqlite_") || kind === "shadow";
155
+ const metaOf = (database) => {
156
+ const values = new Map(database
157
+ .prepare(`SELECT key, value FROM ${SCHEMA}.rollback_meta`)
158
+ .all()
159
+ .map((row) => [String(row.key), JSON.parse(String(row.value))]));
160
+ const tables = values.get("tables");
161
+ return {
162
+ from: Number(values.get("from")),
163
+ to: Number(values.get("to")),
164
+ tables: Array.isArray(tables) ? tables.map(String) : [],
165
+ createdAt: Number(values.get("created_at")),
166
+ };
167
+ };
168
+ /** FTS5 tables over a table's own rows; the other search indexes hold their own text and wait for `store rebuild`. */
169
+ const contentOf = (sql) => /\bcontent\s*=\s*'([^']+)'/i.exec(sql ?? "")?.[1];
170
+ const RANK = { table: 0, index: 1, view: 2, trigger: 3 };
171
+ /**
172
+ * Returns the file to the schema the copy saved: every object made since is dropped, every table the migration
173
+ * changed is put back with its rows, a table only extended since is rebuilt to its old shape, and the history
174
+ * goes back to the copy's version. One transaction: on any failure the store is as it was.
175
+ */
176
+ const rewind = (database, saved, named) => {
177
+ const before = new Map(saved.map((object) => [object.name, object]));
178
+ const same = (object) => before.get(object.name)?.type === object.type && before.get(object.name)?.sql === object.sql;
179
+ const touched = new Set(named);
180
+ const rebuilt = [];
181
+ // A trigger or view over a table dropped or rebuilt below would point nowhere; all of them come back from the copy.
182
+ for (const object of currentObjects(database)) {
183
+ if (object.type === "trigger")
184
+ database.exec(`DROP TRIGGER IF EXISTS ${quote(object.name)}`);
185
+ if (object.type === "view")
186
+ database.exec(`DROP VIEW IF EXISTS ${quote(object.name)}`);
187
+ if (object.type === "index" && object.sql !== null && !same(object))
188
+ database.exec(`DROP INDEX IF EXISTS ${quote(object.name)}`);
189
+ }
190
+ for (const object of currentObjects(database)) {
191
+ if (object.type !== "table" || internal(object))
192
+ continue;
193
+ const old = before.get(object.name);
194
+ if (named.has(object.name) || !old || (object.kind === "virtual" && !same(object))) {
195
+ database.exec(`DROP TABLE ${quote(object.name)}`);
196
+ }
197
+ else if (!same(object) && old.sql) {
198
+ rebuild(database, object.name, old.sql);
199
+ rebuilt.push(object.name);
200
+ touched.add(object.name);
201
+ }
202
+ }
203
+ const present = new Set(currentObjects(database).map(({ name }) => name));
204
+ const missing = saved
205
+ .filter((object) => !internal(object) && object.sql !== null && !present.has(object.name))
206
+ .sort((a, b) => (RANK[a.type] ?? 9) - (RANK[b.type] ?? 9));
207
+ for (const object of missing) {
208
+ database.exec(object.sql);
209
+ if (object.type === "table" && named.has(object.name))
210
+ restoreRows(database, object.name);
211
+ if (object.kind === "virtual")
212
+ touched.add(object.name);
213
+ }
214
+ for (const object of saved) {
215
+ const content = contentOf(object.sql);
216
+ if (object.kind !== "virtual" || !(content && (touched.has(content) || touched.has(object.name))))
217
+ continue;
218
+ database.exec(`INSERT INTO ${quote(object.name)} (${quote(object.name)}) VALUES ('rebuild')`);
219
+ }
220
+ return rebuilt;
221
+ };
222
+ const rebuild = (database, table, sql) => {
223
+ const aside = `__rollback_${table}`;
224
+ database.exec(`ALTER TABLE ${quote(table)} RENAME TO ${quote(aside)}`);
225
+ database.exec(sql);
226
+ const kept = new Set(columnsOf(database, "main", aside));
227
+ const columns = columnsOf(database, "main", table).filter((column) => kept.has(column));
228
+ const rowid = keepsRowid(database, "main", table) ? ["rowid"] : [];
229
+ const list = [...rowid, ...columns.map(quote)].join(", ");
230
+ database.exec(`INSERT INTO ${quote(table)} (${list}) SELECT ${list} FROM ${quote(aside)}`);
231
+ database.exec(`DROP TABLE ${quote(aside)}`);
232
+ };
233
+ const restoreRows = (database, table) => {
234
+ const copied = new Set(columnsOf(database, SCHEMA, table));
235
+ const columns = columnsOf(database, "main", table).filter((column) => copied.has(column));
236
+ const rowid = keepsRowid(database, "main", table) && copied.has(ROWID);
237
+ const into = [...(rowid ? ["rowid"] : []), ...columns.map(quote)].join(", ");
238
+ const from = [...(rowid ? [ROWID] : []), ...columns.map(quote)].join(", ");
239
+ database.exec(`INSERT INTO main.${quote(table)} (${into}) SELECT ${from} FROM ${SCHEMA}.${quote(table)}`);
240
+ };
241
+ /** Puts a rollback copy back into the store, in place. The caller makes sure nothing else has the store open. */
242
+ export const rollBack = (database, path) => {
243
+ attach(database, path);
244
+ let result;
245
+ try {
246
+ if (!tableIn(database, SCHEMA, "rollback_meta"))
247
+ throw new CliError("validation_error", `${path} is not a rollback copy`);
248
+ const meta = metaOf(database);
249
+ const was = versionOf(database);
250
+ if (!database.prepare("SELECT 1 FROM main.schema_migrations WHERE version = ?").get(meta.to)) {
251
+ throw new CliError("validation_error", `the store is at schema ${was} and was never migrated to ${meta.to}, the schema this copy was taken before — nothing to roll back`);
252
+ }
253
+ const saved = objectsOf(database, `SELECT type, name, sql, kind FROM ${SCHEMA}.rollback_schema ORDER BY rowid`);
254
+ database.exec("PRAGMA foreign_keys = OFF");
255
+ // Renaming a table aside must not rewrite the references other tables hold to it.
256
+ database.exec("PRAGMA legacy_alter_table = ON");
257
+ try {
258
+ database.exec("BEGIN IMMEDIATE");
259
+ try {
260
+ const named = new Set(meta.tables);
261
+ const rebuilt = rewind(database, saved, named);
262
+ database.prepare("DELETE FROM main.schema_migrations WHERE version > ?").run(meta.from);
263
+ database.prepare("DELETE FROM main.store_settings WHERE key = ?").run(`${KEY}${basename(path)}`);
264
+ database.exec("COMMIT");
265
+ const count = (table) => Number(database.prepare(`SELECT count(*) AS n FROM main.${quote(table)}`).get()?.n ?? 0);
266
+ result = {
267
+ ...meta,
268
+ was,
269
+ restored: meta.tables.map((table) => ({ table, rows: count(table) })),
270
+ rebuilt,
271
+ };
272
+ }
273
+ catch (error) {
274
+ database.exec("ROLLBACK");
275
+ throw error;
276
+ }
277
+ }
278
+ finally {
279
+ database.exec("PRAGMA legacy_alter_table = OFF");
280
+ database.exec("PRAGMA foreign_keys = ON");
281
+ }
282
+ }
283
+ finally {
284
+ database.exec(`DETACH DATABASE ${SCHEMA}`);
285
+ }
286
+ // Only the tables put back: a check of the whole store reads every row of it.
287
+ const checked = [...result.restored.map(({ table }) => table), ...result.rebuilt];
288
+ const foreignKeyViolations = checked.reduce((total, table) => total + database.prepare(`PRAGMA foreign_key_check(${quote(table)})`).all().length, 0);
289
+ return { ...result, foreignKeyViolations };
290
+ };
@@ -15527,6 +15527,22 @@ export declare const pipelines: import("drizzle-orm/sqlite-core").SQLiteTableWit
15527
15527
  identity: undefined;
15528
15528
  generated: undefined;
15529
15529
  }, {}>;
15530
+ deletedAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
15531
+ name: string;
15532
+ tableName: "pipelines";
15533
+ dataType: "number int53";
15534
+ data: number;
15535
+ driverParam: number;
15536
+ notNull: false;
15537
+ hasDefault: false;
15538
+ isPrimaryKey: false;
15539
+ isAutoincrement: false;
15540
+ hasRuntimeDefault: false;
15541
+ enumValues: undefined;
15542
+ baseColumn: never;
15543
+ identity: undefined;
15544
+ generated: undefined;
15545
+ }, {}>;
15530
15546
  };
15531
15547
  dialect: 'sqlite';
15532
15548
  isAlias: false;
@@ -15679,6 +15695,22 @@ export declare const pipelineStages: import("drizzle-orm/sqlite-core").SQLiteTab
15679
15695
  identity: undefined;
15680
15696
  generated: undefined;
15681
15697
  }, {}>;
15698
+ deletedAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
15699
+ name: string;
15700
+ tableName: "pipeline_stages";
15701
+ dataType: "number int53";
15702
+ data: number;
15703
+ driverParam: number;
15704
+ notNull: false;
15705
+ hasDefault: false;
15706
+ isPrimaryKey: false;
15707
+ isAutoincrement: false;
15708
+ hasRuntimeDefault: false;
15709
+ enumValues: undefined;
15710
+ baseColumn: never;
15711
+ identity: undefined;
15712
+ generated: undefined;
15713
+ }, {}>;
15682
15714
  };
15683
15715
  dialect: 'sqlite';
15684
15716
  isAlias: false;
@@ -1545,9 +1545,10 @@ export const pipelines = sqliteTable("pipelines", {
1545
1545
  nameFolded: text("name_folded").notNull(),
1546
1546
  createdAt: integer("created_at").notNull(),
1547
1547
  updatedAt: integer("updated_at").notNull(),
1548
+ deletedAt: integer("deleted_at"),
1548
1549
  }, (table) => [
1549
- uniqueIndex("pipelines_by_account_id_external_id")
1550
- .on(table.accountId, table.externalId)
1550
+ uniqueIndex("pipelines_by_account_id_kind_external_id")
1551
+ .on(table.accountId, table.kind, table.externalId)
1551
1552
  .where(sql `account_id IS NOT NULL AND external_id IS NOT NULL`),
1552
1553
  uniqueIndex("pipelines_by_name_folded_kind").on(table.nameFolded, table.kind).where(sql `account_id IS NULL`),
1553
1554
  ]);
@@ -1563,6 +1564,7 @@ export const pipelineStages = sqliteTable("pipeline_stages", {
1563
1564
  externalId: text("external_id"),
1564
1565
  createdAt: integer("created_at").notNull(),
1565
1566
  updatedAt: integer("updated_at").notNull(),
1567
+ deletedAt: integer("deleted_at"),
1566
1568
  }, (table) => [
1567
1569
  unique().on(table.pipelineId, table.nameFolded),
1568
1570
  uniqueIndex("pipeline_stages_by_pipeline_id_external_id")
@@ -133,6 +133,7 @@ export interface MessageStore {
133
133
  export type { ChatHistoryShape, StoredHistorySkip } from "./sqlite/history-skips.js";
134
134
  export { BACKFILL_ON_OPEN, type StoreOptions } from "./sqlite/open-store.js";
135
135
  export type { ChatScope, NewestMessage, ReplyDelay, WaitingChat } from "./sqlite/reply-times.js";
136
+ export { rollBack } from "./sqlite/rollback-copy.js";
136
137
  export { fileFacts, readStoreTables, type TableKind } from "./sqlite/store-tables.js";
137
138
  export declare const openStore: (options?: StoreOptions) => Promise<MessageStore>;
138
139
  export * from "./exports.js";
@@ -50,6 +50,7 @@ import { syncStoreOver } from "./sync.js";
50
50
  import { tagsStoreOver } from "./tags.js";
51
51
  import { accountVectorsOver, vectorsStoreOver } from "./vectors.js";
52
52
  export { BACKFILL_ON_OPEN } from "./sqlite/open-store.js";
53
+ export { rollBack } from "./sqlite/rollback-copy.js";
53
54
  export { fileFacts, readStoreTables } from "./sqlite/store-tables.js";
54
55
  export const openStore = async (options = {}) => {
55
56
  const context = await openStoreContext(options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-core",
3
- "version": "0.593.0",
3
+ "version": "0.595.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",
@@ -270,7 +270,7 @@
270
270
  "@types/node": "^22.20.5",
271
271
  "@types/pngjs": "6.0.5",
272
272
  "@vitest/coverage-v8": "^5.0.5",
273
- "@wirecat/cli-base": "0.80.0",
273
+ "@wirecat/cli-base": "0.80.2",
274
274
  "@wirecat/cli-core-legacy-tags": "npm:@wirecat/cli-core@0.414.0",
275
275
  "commander": "15.0.0",
276
276
  "cspell": "10.4.0",