@wirecat/cli-core 0.543.0 → 0.545.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 (36) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/cli/config-command.js +32 -2
  3. package/dist/cli/messenger/archive-commands.js +20 -2
  4. package/dist/cli/messenger/backfill-command.js +13 -2
  5. package/dist/cli/messenger/context.js +3 -0
  6. package/dist/cli/messenger/history-gaps.d.ts +12 -0
  7. package/dist/cli/messenger/history-gaps.js +41 -0
  8. package/dist/cli/messenger/messages-command.js +7 -3
  9. package/dist/cli/messenger/messages-search-command.js +5 -1
  10. package/dist/cli/messenger/search-answers.d.ts +3 -2
  11. package/dist/cli/messenger/search-answers.js +12 -5
  12. package/dist/cli/messenger/search-command.js +3 -1
  13. package/dist/cli/messenger/stored.d.ts +6 -1
  14. package/dist/cli/messenger/stored.js +38 -2
  15. package/dist/cli/program.js +22 -0
  16. package/dist/cli/settings.d.ts +9 -0
  17. package/dist/cli/settings.js +20 -0
  18. package/dist/cli/shared-command-exports.d.ts +24 -0
  19. package/dist/cli/skill-command.d.ts +2 -0
  20. package/dist/cli/skill-command.js +10 -0
  21. package/dist/cli/skill-refresh.d.ts +18 -0
  22. package/dist/cli/skill-refresh.js +82 -0
  23. package/dist/cli/steps.js +1 -0
  24. package/dist/entities/index.d.ts +1 -0
  25. package/dist/entities/message-transfer.d.ts +4 -0
  26. package/dist/entities/message-transfer.js +2 -0
  27. package/dist/services/history-limits.d.ts +45 -0
  28. package/dist/services/history-limits.js +50 -0
  29. package/dist/store/chats.d.ts +10 -0
  30. package/dist/store/chats.js +25 -0
  31. package/dist/store/sqlite/chats.js +2 -1
  32. package/dist/store/sqlite/history-skips.d.ts +24 -0
  33. package/dist/store/sqlite/history-skips.js +95 -0
  34. package/dist/store/store.d.ts +1 -0
  35. package/package.json +1 -1
  36. package/parity.json +4 -0
@@ -0,0 +1,95 @@
1
+ /**
2
+ * The history limits' record of a chat, kept in the chat row's `metadata` under this key so no store version is
3
+ * needed; `Chat.providerMetadata` leaves it out.
4
+ */
5
+ export const HISTORY_KEY = "$history";
6
+ const objectOf = (text) => {
7
+ if (typeof text !== "string")
8
+ return {};
9
+ try {
10
+ const value = JSON.parse(text);
11
+ return value !== null && typeof value === "object" && !Array.isArray(value)
12
+ ? value
13
+ : {};
14
+ }
15
+ catch {
16
+ return {};
17
+ }
18
+ };
19
+ const recordOf = (metadata) => {
20
+ const value = objectOf(metadata)[HISTORY_KEY];
21
+ return value !== null && typeof value === "object" ? value : {};
22
+ };
23
+ const rowOf = ({ database }, accountKey, chatId) => database
24
+ .prepare("SELECT id, kind, participants_count, metadata FROM chats WHERE account_id = ? AND external_id = ?")
25
+ .get(accountKey, chatId);
26
+ const update = (context, id, metadata, change) => {
27
+ const all = objectOf(metadata);
28
+ const next = change(recordOf(metadata));
29
+ if (next.skipped === undefined && !next.included)
30
+ delete all[HISTORY_KEY];
31
+ else
32
+ all[HISTORY_KEY] = next;
33
+ const text = Object.keys(all).length === 0 ? null : JSON.stringify(all);
34
+ context.database.prepare("UPDATE chats SET metadata = ?, updated_at = ? WHERE id = ?").run(text, context.now(), id);
35
+ };
36
+ export const historyShapes = (context, accountKey, chatIds) => {
37
+ const shapes = new Map();
38
+ for (const chatId of new Set(chatIds)) {
39
+ const row = rowOf(context, accountKey, chatId);
40
+ if (!row)
41
+ continue;
42
+ shapes.set(chatId, {
43
+ kind: String(row.kind),
44
+ participantsCount: row.participants_count == null ? null : Number(row.participants_count),
45
+ included: recordOf(row.metadata).included === true,
46
+ });
47
+ }
48
+ return shapes;
49
+ };
50
+ /** Adds what one push left out to what the chat's record already holds. A chat the store lacks is left as it is. */
51
+ export const recordHistorySkips = (context, accountKey, skips) => {
52
+ for (const [chatId, skip] of skips) {
53
+ const row = rowOf(context, accountKey, chatId);
54
+ if (!row)
55
+ continue;
56
+ update(context, Number(row.id), row.metadata, (held) => {
57
+ const before = held.skipped;
58
+ const skipped = before
59
+ ? {
60
+ ...skip,
61
+ messages: before.messages + skip.messages,
62
+ oldestAt: before.oldestAt < skip.oldestAt ? before.oldestAt : skip.oldestAt,
63
+ newestAt: before.newestAt > skip.newestAt ? before.newestAt : skip.newestAt,
64
+ }
65
+ : skip;
66
+ return { ...held, skipped };
67
+ });
68
+ }
69
+ };
70
+ /** Lifts the limits for one chat from now on, or puts them back. Answers `false` for a chat the store lacks. */
71
+ export const setHistoryIncluded = (context, accountKey, chatId, included) => {
72
+ const row = rowOf(context, accountKey, chatId);
73
+ if (!row)
74
+ return false;
75
+ update(context, Number(row.id), row.metadata, (held) => ({ ...held, included }));
76
+ return true;
77
+ };
78
+ /** Every chat of the account whose pushed history the limits left out, most messages first. */
79
+ export const historySkips = ({ database }, accountKey) => database
80
+ .prepare(`SELECT external_id, title, metadata FROM chats WHERE account_id = ? AND metadata LIKE '%"${HISTORY_KEY}"%'`)
81
+ .all(accountKey)
82
+ .flatMap((row) => {
83
+ const { skipped } = recordOf(row.metadata);
84
+ return skipped
85
+ ? [{ chatId: String(row.external_id), title: row.title == null ? null : String(row.title), ...skipped }]
86
+ : [];
87
+ })
88
+ .sort((a, b) => b.messages - a.messages);
89
+ /** `metadata` as the messenger gave it: the history record is the store's own. */
90
+ export const withoutHistory = (metadata) => {
91
+ if (metadata === undefined || !(HISTORY_KEY in metadata))
92
+ return metadata;
93
+ const { [HISTORY_KEY]: _, ...rest } = metadata;
94
+ return rest;
95
+ };
@@ -106,6 +106,7 @@ export interface MessageStore {
106
106
  forAccount(key: AccountKey): AccountStore;
107
107
  close(): Promise<void>;
108
108
  }
109
+ export type { ChatHistoryShape, StoredHistorySkip } from "./sqlite/history-skips.js";
109
110
  export { BACKFILL_ON_OPEN, type StoreOptions } from "./sqlite/open-store.js";
110
111
  export { fileFacts, readStoreTables, type TableKind } from "./sqlite/store-tables.js";
111
112
  export declare const openStore: (options?: StoreOptions) => Promise<MessageStore>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-core",
3
- "version": "0.543.0",
3
+ "version": "0.545.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",
package/parity.json CHANGED
@@ -438,6 +438,9 @@
438
438
  "--full": {
439
439
  "meaning": "found messages with their whole text and every provider field"
440
440
  },
441
+ "--full-history": {
442
+ "meaning": "keep this chat's pushed history in full from now on, past the history limits"
443
+ },
441
444
  "--hidden": {
442
445
  "value": "<on|off>",
443
446
  "meaning": "on hides a forum's General topic from the topic list, off shows it"
@@ -3509,6 +3512,7 @@
3509
3512
  "--catch-up-messages": "all",
3510
3513
  "--catch-up-time": "all",
3511
3514
  "--dry-run": "all",
3515
+ "--full-history": "all",
3512
3516
  "--last": "all",
3513
3517
  "--limit": "all",
3514
3518
  "--no-catch-up": "all",