@wirecat/cli-core 0.550.0 → 0.552.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 +235 -0
- package/README.md +1 -1
- package/THIRD_PARTY_NOTICES +41 -0
- package/dist/background/schedules.d.ts +12 -1
- package/dist/background/schedules.js +65 -24
- package/dist/background/units.d.ts +16 -9
- package/dist/background/units.js +26 -11
- package/dist/calendar/ics.d.ts +1 -1
- package/dist/calendar/ics.js +5 -2
- package/dist/calendar/windows-zones.generated.d.ts +1 -0
- package/dist/calendar/windows-zones.generated.js +146 -0
- package/dist/cli/compact.js +2 -0
- package/dist/cli/context.d.ts +2 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/meeting-findings-input.js +4 -3
- package/dist/cli/meeting-preparation-answer.d.ts +3 -0
- package/dist/cli/meeting-preparation-projects.d.ts +3 -0
- package/dist/cli/meeting-preparation-read.d.ts +3 -0
- package/dist/cli/messenger/backfill-command.js +28 -1
- package/dist/cli/messenger/context.js +1 -0
- package/dist/cli/messenger/history-gaps.js +4 -4
- package/dist/cli/messenger/mcp-command.js +1 -0
- package/dist/cli/messenger/messages-delete-command.js +7 -8
- package/dist/cli/messenger/projects-command.js +47 -2
- package/dist/cli/messenger/search-answers.js +8 -1
- package/dist/cli/messenger/serve-catch-up.d.ts +43 -0
- package/dist/cli/messenger/serve-catch-up.js +90 -0
- package/dist/cli/messenger/serve-command.js +54 -1
- package/dist/cli/messenger/server-command.d.ts +4 -3
- package/dist/cli/messenger/server-command.js +30 -12
- package/dist/cli/payment-query.d.ts +8 -0
- package/dist/cli/payment-query.js +27 -0
- package/dist/cli/program.js +9 -4
- package/dist/cli/project-status-rows.d.ts +8 -0
- package/dist/cli/project-status-rows.js +102 -0
- package/dist/cli/project-status.d.ts +132 -0
- package/dist/cli/project-status.js +248 -0
- package/dist/cli/schedule/schedule-command.js +5 -1
- package/dist/cli/steps.js +4 -1
- package/dist/domain/knowledge.d.ts +1 -1
- package/dist/domain/knowledge.js +1 -1
- package/dist/domain/status-words.d.ts +3 -0
- package/dist/domain/status-words.js +44 -0
- package/dist/entities/fact.d.ts +12 -1
- package/dist/entities/fact.js +13 -0
- package/dist/entities/hit.d.ts +5 -0
- package/dist/entities/hit.js +5 -0
- package/dist/entities/index.d.ts +45 -2
- package/dist/entities/listener.d.ts +6 -0
- package/dist/entities/listener.js +1 -0
- package/dist/entities/meeting-findings.d.ts +1 -1
- package/dist/entities/meeting-preparation.d.ts +3 -0
- package/dist/entities/message-transfer.d.ts +16 -0
- package/dist/entities/message-transfer.js +6 -0
- package/dist/errors/reasons.js +1 -0
- package/dist/mcp/app-server.js +1 -0
- package/dist/mcp/http/oauth.d.ts +2 -1
- package/dist/mcp/http/oauth.js +2 -2
- package/dist/mcp/http/serve.d.ts +2 -0
- package/dist/mcp/http/serve.js +2 -1
- package/dist/mcp/meeting-preparation.d.ts +3 -0
- package/dist/mcp/server.js +1 -0
- package/dist/mcp/surface.js +1 -1
- package/dist/meetings/answers.d.ts +5 -0
- package/dist/meetings/answers.js +8 -0
- package/dist/meetings/cli/index.js +14 -0
- package/dist/meetings/findings-rules.d.ts +14 -3
- package/dist/meetings/findings-rules.js +16 -2
- package/dist/meetings/index.d.ts +5 -3
- package/dist/meetings/index.js +3 -1
- package/dist/meetings/ingest.js +2 -1
- package/dist/meetings/language.d.ts +5 -0
- package/dist/meetings/language.js +57 -0
- package/dist/meetings/mcp/search.d.ts +3 -0
- package/dist/meetings/meeting-context.d.ts +2 -3
- package/dist/meetings/meeting-context.js +3 -4
- package/dist/meetings/payment.d.ts +27 -0
- package/dist/meetings/payment.js +81 -0
- package/dist/meetings/project-evidence-answer.d.ts +3 -0
- package/dist/meetings/result-items.d.ts +9 -0
- package/dist/meetings/result-items.js +7 -0
- package/dist/meetings/store.d.ts +22 -0
- package/dist/meetings/testing/memory.js +3 -0
- package/dist/meetings/transcription.d.ts +2 -0
- package/dist/meetings/transcription.js +5 -1
- package/dist/search/lucene/types.d.ts +7 -0
- package/dist/search/lucene/types.js +11 -1
- package/dist/services/archive.d.ts +13 -0
- package/dist/services/archive.js +56 -5
- package/dist/services/deps.d.ts +3 -0
- package/dist/services/history-limits.d.ts +2 -0
- package/dist/services/index.d.ts +4 -1
- package/dist/services/index.js +4 -1
- package/dist/services/job-query.d.ts +5 -2
- package/dist/services/job-query.js +5 -2
- package/dist/services/link-scorers/alias-hits.d.ts +7 -0
- package/dist/services/link-scorers/alias-hits.js +54 -0
- package/dist/services/link-scorers/chat-senders.d.ts +6 -0
- package/dist/services/link-scorers/chat-senders.js +56 -0
- package/dist/services/link-scorers/chat-title.d.ts +6 -0
- package/dist/services/link-scorers/chat-title.js +44 -0
- package/dist/services/link-scorers/email-domain.d.ts +6 -0
- package/dist/services/link-scorers/email-domain.js +64 -0
- package/dist/services/link-scorers/index.d.ts +9 -0
- package/dist/services/link-scorers/index.js +8 -0
- package/dist/services/link-scorers/mail-thread.d.ts +3 -0
- package/dist/services/link-scorers/mail-thread.js +74 -0
- package/dist/services/link-scorers/member-chats.d.ts +3 -0
- package/dist/services/link-scorers/member-chats.js +40 -0
- package/dist/services/link-scorers/member-dialog.d.ts +3 -0
- package/dist/services/link-scorers/member-dialog.js +34 -0
- package/dist/services/link-scorers/scan.d.ts +41 -0
- package/dist/services/link-scorers/scan.js +244 -0
- package/dist/services/link-scorers/shared.d.ts +26 -0
- package/dist/services/link-scorers/shared.js +48 -0
- package/dist/services/link-scorers/types.d.ts +77 -0
- package/dist/services/link-scorers/types.js +1 -0
- package/dist/services/messages-archive-search.js +3 -2
- package/dist/services/messages-combined.js +2 -2
- package/dist/services/messages-discovery.js +2 -2
- package/dist/services/notes-search.js +4 -5
- package/dist/services/project-status/index.d.ts +71 -0
- package/dist/services/project-status/index.js +196 -0
- package/dist/services/project-status/items.d.ts +10 -0
- package/dist/services/project-status/items.js +23 -0
- package/dist/services/project-status/signals/amounts.d.ts +20 -0
- package/dist/services/project-status/signals/amounts.js +91 -0
- package/dist/services/project-status/signals/children.d.ts +13 -0
- package/dist/services/project-status/signals/children.js +28 -0
- package/dist/services/project-status/signals/coverage.d.ts +13 -0
- package/dist/services/project-status/signals/coverage.js +30 -0
- package/dist/services/project-status/signals/index.d.ts +3 -0
- package/dist/services/project-status/signals/index.js +21 -0
- package/dist/services/project-status/signals/last-message.d.ts +4 -0
- package/dist/services/project-status/signals/last-message.js +22 -0
- package/dist/services/project-status/signals/payments.d.ts +9 -0
- package/dist/services/project-status/signals/payments.js +24 -0
- package/dist/services/project-status/signals/promises.d.ts +17 -0
- package/dist/services/project-status/signals/promises.js +109 -0
- package/dist/services/project-status/signals/questions.d.ts +8 -0
- package/dist/services/project-status/signals/questions.js +38 -0
- package/dist/services/project-status/signals/reply-next.d.ts +19 -0
- package/dist/services/project-status/signals/reply-next.js +75 -0
- package/dist/services/project-status/signals/topics.d.ts +10 -0
- package/dist/services/project-status/signals/topics.js +50 -0
- package/dist/services/project-status/types.d.ts +84 -0
- package/dist/services/project-status/types.js +1 -0
- package/dist/services/proposals/index.d.ts +1 -0
- package/dist/services/proposals/index.js +1 -0
- package/dist/services/proposals/kinds/findings.d.ts +3 -0
- package/dist/services/proposals/kinds/findings.js +8 -1
- package/dist/services/search-all-meetings.d.ts +3 -0
- package/dist/services/search-all-meetings.js +6 -1
- package/dist/speech/local.d.ts +2 -0
- package/dist/speech/local.js +8 -1
- package/dist/speech/models.d.ts +2 -0
- package/dist/speech/models.js +2 -0
- package/dist/store/decisions.d.ts +5 -1
- package/dist/store/decisions.js +5 -1
- package/dist/store/driver.d.ts +11 -0
- package/dist/store/driver.js +13 -0
- package/dist/store/drivers/node-sqlite.d.ts +1 -1
- package/dist/store/drivers/node-sqlite.js +3 -0
- package/dist/store/evidence-findings.js +1 -0
- package/dist/store/index.d.ts +2 -1
- package/dist/store/index.js +1 -0
- package/dist/store/project-transfer/request.d.ts +1 -1
- package/dist/store/project-transfer/schema.d.ts +2 -2
- package/dist/store/project-transfer/snapshot-model.d.ts +2 -2
- package/dist/store/sqlite/evidence-findings.js +5 -1
- package/dist/store/sqlite/facts.d.ts +13 -0
- package/dist/store/sqlite/facts.js +64 -6
- package/dist/store/sqlite/findings-core.js +14 -1
- package/dist/store/sqlite/knowledge.d.ts +16 -2
- package/dist/store/sqlite/knowledge.js +1 -0
- package/dist/store/sqlite/lucene.d.ts +1 -1
- package/dist/store/sqlite/lucene.js +33 -7
- package/dist/store/sqlite/meeting-hit-search.js +10 -3
- package/dist/store/sqlite/meeting-languages.d.ts +4 -0
- package/dist/store/sqlite/meeting-languages.js +36 -0
- package/dist/store/sqlite/meetings.d.ts +2 -1
- package/dist/store/sqlite/meetings.js +9 -3
- package/dist/store/sqlite/note-search.js +2 -2
- package/dist/store/sqlite/project-transfer/snapshot.d.ts +1 -1
- package/dist/store/sqlite/relation-proposals.d.ts +29 -2
- package/dist/store/sqlite/relation-proposals.js +75 -4
- package/dist/store/store.d.ts +1 -0
- package/package.json +3 -3
- package/parity.json +11 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const WINDOWS_ZONES: Readonly<Record<string, string>>;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// Generated by scripts/windows-zones.ts from CLDR release-49 (Unicode License v3, THIRD_PARTY_NOTICES); do not edit by hand.
|
|
2
|
+
// https://raw.githubusercontent.com/unicode-org/cldr/release-49/common/supplemental/windowsZones.xml
|
|
3
|
+
export const WINDOWS_ZONES = {
|
|
4
|
+
"Afghanistan Standard Time": "Asia/Kabul",
|
|
5
|
+
"Alaskan Standard Time": "America/Anchorage",
|
|
6
|
+
"Alberta Standard Time": "America/Edmonton",
|
|
7
|
+
"Aleutian Standard Time": "America/Adak",
|
|
8
|
+
"Altai Standard Time": "Asia/Barnaul",
|
|
9
|
+
"Arab Standard Time": "Asia/Riyadh",
|
|
10
|
+
"Arabian Standard Time": "Asia/Dubai",
|
|
11
|
+
"Arabic Standard Time": "Asia/Baghdad",
|
|
12
|
+
"Argentina Standard Time": "America/Buenos_Aires",
|
|
13
|
+
"Astrakhan Standard Time": "Europe/Astrakhan",
|
|
14
|
+
"Atlantic Standard Time": "America/Halifax",
|
|
15
|
+
"AUS Central Standard Time": "Australia/Darwin",
|
|
16
|
+
"Aus Central W. Standard Time": "Australia/Eucla",
|
|
17
|
+
"AUS Eastern Standard Time": "Australia/Sydney",
|
|
18
|
+
"Azerbaijan Standard Time": "Asia/Baku",
|
|
19
|
+
"Azores Standard Time": "Atlantic/Azores",
|
|
20
|
+
"Bahia Standard Time": "America/Bahia",
|
|
21
|
+
"Bangladesh Standard Time": "Asia/Dhaka",
|
|
22
|
+
"Belarus Standard Time": "Europe/Minsk",
|
|
23
|
+
"Bougainville Standard Time": "Pacific/Bougainville",
|
|
24
|
+
"British Columbia Standard Time": "America/Vancouver",
|
|
25
|
+
"Canada Central Standard Time": "America/Regina",
|
|
26
|
+
"Cape Verde Standard Time": "Atlantic/Cape_Verde",
|
|
27
|
+
"Caucasus Standard Time": "Asia/Yerevan",
|
|
28
|
+
"Cen. Australia Standard Time": "Australia/Adelaide",
|
|
29
|
+
"Central America Standard Time": "America/Guatemala",
|
|
30
|
+
"Central Asia Standard Time": "Asia/Bishkek",
|
|
31
|
+
"Central Brazilian Standard Time": "America/Cuiaba",
|
|
32
|
+
"Central Europe Standard Time": "Europe/Budapest",
|
|
33
|
+
"Central European Standard Time": "Europe/Warsaw",
|
|
34
|
+
"Central Pacific Standard Time": "Pacific/Guadalcanal",
|
|
35
|
+
"Central Standard Time": "America/Chicago",
|
|
36
|
+
"Central Standard Time (Mexico)": "America/Mexico_City",
|
|
37
|
+
"Chatham Islands Standard Time": "Pacific/Chatham",
|
|
38
|
+
"China Standard Time": "Asia/Shanghai",
|
|
39
|
+
"Cuba Standard Time": "America/Havana",
|
|
40
|
+
"Dateline Standard Time": "Etc/GMT+12",
|
|
41
|
+
"E. Africa Standard Time": "Africa/Nairobi",
|
|
42
|
+
"E. Australia Standard Time": "Australia/Brisbane",
|
|
43
|
+
"E. Europe Standard Time": "Europe/Chisinau",
|
|
44
|
+
"E. South America Standard Time": "America/Sao_Paulo",
|
|
45
|
+
"Easter Island Standard Time": "Pacific/Easter",
|
|
46
|
+
"Eastern Standard Time": "America/New_York",
|
|
47
|
+
"Eastern Standard Time (Mexico)": "America/Cancun",
|
|
48
|
+
"Egypt Standard Time": "Africa/Cairo",
|
|
49
|
+
"Ekaterinburg Standard Time": "Asia/Yekaterinburg",
|
|
50
|
+
"Fiji Standard Time": "Pacific/Fiji",
|
|
51
|
+
"FLE Standard Time": "Europe/Kiev",
|
|
52
|
+
"Georgian Standard Time": "Asia/Tbilisi",
|
|
53
|
+
"GMT Standard Time": "Europe/London",
|
|
54
|
+
"Greenland Standard Time": "America/Godthab",
|
|
55
|
+
"Greenwich Standard Time": "Atlantic/Reykjavik",
|
|
56
|
+
"GTB Standard Time": "Europe/Bucharest",
|
|
57
|
+
"Haiti Standard Time": "America/Port-au-Prince",
|
|
58
|
+
"Hawaiian Standard Time": "Pacific/Honolulu",
|
|
59
|
+
"India Standard Time": "Asia/Calcutta",
|
|
60
|
+
"Iran Standard Time": "Asia/Tehran",
|
|
61
|
+
"Israel Standard Time": "Asia/Jerusalem",
|
|
62
|
+
"Jordan Standard Time": "Asia/Amman",
|
|
63
|
+
"Kaliningrad Standard Time": "Europe/Kaliningrad",
|
|
64
|
+
"Korea Standard Time": "Asia/Seoul",
|
|
65
|
+
"Libya Standard Time": "Africa/Tripoli",
|
|
66
|
+
"Line Islands Standard Time": "Pacific/Kiritimati",
|
|
67
|
+
"Lord Howe Standard Time": "Australia/Lord_Howe",
|
|
68
|
+
"Magadan Standard Time": "Asia/Magadan",
|
|
69
|
+
"Magallanes Standard Time": "America/Punta_Arenas",
|
|
70
|
+
"Manitoba Standard Time": "America/Winnipeg",
|
|
71
|
+
"Marquesas Standard Time": "Pacific/Marquesas",
|
|
72
|
+
"Mauritius Standard Time": "Indian/Mauritius",
|
|
73
|
+
"Middle East Standard Time": "Asia/Beirut",
|
|
74
|
+
"Montevideo Standard Time": "America/Montevideo",
|
|
75
|
+
"Morocco Standard Time": "Africa/Casablanca",
|
|
76
|
+
"Mountain Standard Time": "America/Denver",
|
|
77
|
+
"Mountain Standard Time (Mexico)": "America/Mazatlan",
|
|
78
|
+
"Myanmar Standard Time": "Asia/Rangoon",
|
|
79
|
+
"N. Central Asia Standard Time": "Asia/Novosibirsk",
|
|
80
|
+
"Namibia Standard Time": "Africa/Windhoek",
|
|
81
|
+
"Nepal Standard Time": "Asia/Katmandu",
|
|
82
|
+
"New Zealand Standard Time": "Pacific/Auckland",
|
|
83
|
+
"Newfoundland Standard Time": "America/St_Johns",
|
|
84
|
+
"Norfolk Standard Time": "Pacific/Norfolk",
|
|
85
|
+
"North Asia East Standard Time": "Asia/Irkutsk",
|
|
86
|
+
"North Asia Standard Time": "Asia/Krasnoyarsk",
|
|
87
|
+
"North Korea Standard Time": "Asia/Pyongyang",
|
|
88
|
+
"Omsk Standard Time": "Asia/Omsk",
|
|
89
|
+
"Pacific SA Standard Time": "America/Santiago",
|
|
90
|
+
"Pacific Standard Time": "America/Los_Angeles",
|
|
91
|
+
"Pacific Standard Time (Mexico)": "America/Tijuana",
|
|
92
|
+
"Pakistan Standard Time": "Asia/Karachi",
|
|
93
|
+
"Paraguay Standard Time": "America/Asuncion",
|
|
94
|
+
"Qyzylorda Standard Time": "Asia/Qyzylorda",
|
|
95
|
+
"Romance Standard Time": "Europe/Paris",
|
|
96
|
+
"Russia Time Zone 10": "Asia/Srednekolymsk",
|
|
97
|
+
"Russia Time Zone 11": "Asia/Kamchatka",
|
|
98
|
+
"Russia Time Zone 3": "Europe/Samara",
|
|
99
|
+
"Russian Standard Time": "Europe/Moscow",
|
|
100
|
+
"SA Eastern Standard Time": "America/Cayenne",
|
|
101
|
+
"SA Pacific Standard Time": "America/Bogota",
|
|
102
|
+
"SA Western Standard Time": "America/La_Paz",
|
|
103
|
+
"Saint Pierre Standard Time": "America/Miquelon",
|
|
104
|
+
"Sakhalin Standard Time": "Asia/Sakhalin",
|
|
105
|
+
"Samoa Standard Time": "Pacific/Apia",
|
|
106
|
+
"Sao Tome Standard Time": "Africa/Sao_Tome",
|
|
107
|
+
"Saratov Standard Time": "Europe/Saratov",
|
|
108
|
+
"SE Asia Standard Time": "Asia/Bangkok",
|
|
109
|
+
"Singapore Standard Time": "Asia/Singapore",
|
|
110
|
+
"South Africa Standard Time": "Africa/Johannesburg",
|
|
111
|
+
"South Sudan Standard Time": "Africa/Juba",
|
|
112
|
+
"Sri Lanka Standard Time": "Asia/Colombo",
|
|
113
|
+
"Sudan Standard Time": "Africa/Khartoum",
|
|
114
|
+
"Syria Standard Time": "Asia/Damascus",
|
|
115
|
+
"Taipei Standard Time": "Asia/Taipei",
|
|
116
|
+
"Tasmania Standard Time": "Australia/Hobart",
|
|
117
|
+
"Tocantins Standard Time": "America/Araguaina",
|
|
118
|
+
"Tokyo Standard Time": "Asia/Tokyo",
|
|
119
|
+
"Tomsk Standard Time": "Asia/Tomsk",
|
|
120
|
+
"Tonga Standard Time": "Pacific/Tongatapu",
|
|
121
|
+
"Transbaikal Standard Time": "Asia/Chita",
|
|
122
|
+
"Turkey Standard Time": "Europe/Istanbul",
|
|
123
|
+
"Turks And Caicos Standard Time": "America/Grand_Turk",
|
|
124
|
+
"Ulaanbaatar Standard Time": "Asia/Ulaanbaatar",
|
|
125
|
+
"US Eastern Standard Time": "America/Indianapolis",
|
|
126
|
+
"US Mountain Standard Time": "America/Phoenix",
|
|
127
|
+
UTC: "Etc/UTC",
|
|
128
|
+
"UTC-02": "Etc/GMT+2",
|
|
129
|
+
"UTC-08": "Etc/GMT+8",
|
|
130
|
+
"UTC-09": "Etc/GMT+9",
|
|
131
|
+
"UTC-11": "Etc/GMT+11",
|
|
132
|
+
"UTC+12": "Etc/GMT-12",
|
|
133
|
+
"UTC+13": "Etc/GMT-13",
|
|
134
|
+
"Venezuela Standard Time": "America/Caracas",
|
|
135
|
+
"Vladivostok Standard Time": "Asia/Vladivostok",
|
|
136
|
+
"Volgograd Standard Time": "Europe/Volgograd",
|
|
137
|
+
"W. Australia Standard Time": "Australia/Perth",
|
|
138
|
+
"W. Central Africa Standard Time": "Africa/Lagos",
|
|
139
|
+
"W. Europe Standard Time": "Europe/Berlin",
|
|
140
|
+
"W. Mongolia Standard Time": "Asia/Hovd",
|
|
141
|
+
"West Asia Standard Time": "Asia/Tashkent",
|
|
142
|
+
"West Bank Standard Time": "Asia/Hebron",
|
|
143
|
+
"West Pacific Standard Time": "Pacific/Port_Moresby",
|
|
144
|
+
"Yakutsk Standard Time": "Asia/Yakutsk",
|
|
145
|
+
"Yukon Standard Time": "America/Whitehorse",
|
|
146
|
+
};
|
package/dist/cli/compact.js
CHANGED
|
@@ -2,6 +2,7 @@ import { visibleControls } from "@wirecat/cli-base";
|
|
|
2
2
|
import { metaOf } from "@wirecat/cli-base/commands";
|
|
3
3
|
import { ENTITIES } from "../entities/index.js";
|
|
4
4
|
import { answerRow } from "./answer-entity.js";
|
|
5
|
+
import { STATUS_ROWS } from "./project-status-rows.js";
|
|
5
6
|
export const TEXT_CHARS = 400;
|
|
6
7
|
const sender = { field: ["sender_is_me", "sender_name"], tag: "me" };
|
|
7
8
|
const messageRow = {
|
|
@@ -39,6 +40,7 @@ export const SHARED_ROWS = {
|
|
|
39
40
|
{ field: "last_message_at", as: "time" },
|
|
40
41
|
],
|
|
41
42
|
},
|
|
43
|
+
...STATUS_ROWS,
|
|
42
44
|
};
|
|
43
45
|
/** Any record: what names it, what it is called, what state it is in, then what it says. */
|
|
44
46
|
export const GENERIC_ROW = {
|
package/dist/cli/context.d.ts
CHANGED
|
@@ -64,6 +64,8 @@ export interface BaseEnvironment {
|
|
|
64
64
|
* message was handled, and nothing was sent" instead of on the next event or a sleep.
|
|
65
65
|
*/
|
|
66
66
|
handled?: (event: MessageEvent) => void;
|
|
67
|
+
/** A test's hook into `serve`: how long it waits after connecting before its catch-up pass. */
|
|
68
|
+
startSyncDelayMs?: number;
|
|
67
69
|
/** Set by an MCP server on a command it runs for an agent (`runCommandForAgent`). */
|
|
68
70
|
agent?: AgentRun;
|
|
69
71
|
/** Set by `run()`: the words it was given, which a run keeps as `record_input` says (`maskArgv`). */
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -111,10 +111,13 @@ export { type Saving, stored } from "./messenger/stored.js";
|
|
|
111
111
|
export { dateOrNone } from "./messenger/tasks-command.js";
|
|
112
112
|
export { MODEL_CATALOGUE_QUERY, type ModelCatalogueOptions } from "./model-catalogue-command.js";
|
|
113
113
|
export { positiveCount, window, withPaging } from "./paging.js";
|
|
114
|
+
export { PAYMENT_QUERY_FIELDS, paymentFilterOf } from "./payment-query.js";
|
|
114
115
|
export { type CommandPermissions, commandPathKey, commandPermissions, knownBeside, knownPermissionKeys, type PermissionKeyOf, type PermissionKeySource, refuseUnknownPermissionKeys, unknownPermissionIssues, unknownPermissionKeys, } from "./permission-keys.js";
|
|
115
116
|
export { asFirstWord, commandWords, liftProfile, profileFree, refuseCommandName, rootOf, takesProfile, usableProfileName, } from "./profile.js";
|
|
116
117
|
export { createProgram, type ProgramDefinition, type ProgramOptions, type RunOptions, run } from "./program.js";
|
|
117
118
|
export { type ProjectTextHints, projectAnswerParts, projectCutoffTime, projectEvidenceText, projectWindowOf, withSyncCommands, } from "./project-evidence.js";
|
|
119
|
+
export { type ProjectStatusHints, type ProjectStatusItem, projectStatusAnswer, projectStatusRow, projectStatusSchema, projectStatusText, } from "./project-status.js";
|
|
120
|
+
export { STATUS_ROWS, STATUS_SIGNAL_ROWS } from "./project-status-rows.js";
|
|
118
121
|
export { fieldsOf, projectFields } from "./result-fields.js";
|
|
119
122
|
export type { ScheduleHost } from "./schedule/schedule-command.js";
|
|
120
123
|
export { type Config, type Configuration, count, first, flag, fromFile, type GlobalFlags, NONE_NAMED, namedProfile, type ProfileKind, plain, type ResolveOptions, type Settings, type SettingsExtension, type SettingsExtensionContext, type Source, settingsFor, } from "./settings.js";
|
package/dist/cli/index.js
CHANGED
|
@@ -101,10 +101,13 @@ export { stored } from "./messenger/stored.js";
|
|
|
101
101
|
export { dateOrNone } from "./messenger/tasks-command.js";
|
|
102
102
|
export { MODEL_CATALOGUE_QUERY } from "./model-catalogue-command.js";
|
|
103
103
|
export { positiveCount, window, withPaging } from "./paging.js";
|
|
104
|
+
export { PAYMENT_QUERY_FIELDS, paymentFilterOf } from "./payment-query.js";
|
|
104
105
|
export { commandPathKey, commandPermissions, knownBeside, knownPermissionKeys, refuseUnknownPermissionKeys, unknownPermissionIssues, unknownPermissionKeys, } from "./permission-keys.js";
|
|
105
106
|
export { asFirstWord, commandWords, liftProfile, profileFree, refuseCommandName, rootOf, takesProfile, usableProfileName, } from "./profile.js";
|
|
106
107
|
export { createProgram, run } from "./program.js";
|
|
107
108
|
export { projectAnswerParts, projectCutoffTime, projectEvidenceText, projectWindowOf, withSyncCommands, } from "./project-evidence.js";
|
|
109
|
+
export { projectStatusAnswer, projectStatusRow, projectStatusSchema, projectStatusText, } from "./project-status.js";
|
|
110
|
+
export { STATUS_ROWS, STATUS_SIGNAL_ROWS } from "./project-status-rows.js";
|
|
108
111
|
export { fieldsOf, projectFields } from "./result-fields.js";
|
|
109
112
|
export { count, first, flag, fromFile, NONE_NAMED, namedProfile, plain, settingsFor, } from "./settings.js";
|
|
110
113
|
export { readSetupAnswers, setupPermissionCeiling } from "./setup-answers.js";
|
|
@@ -17,7 +17,8 @@ const parseFindingsJson = (text, label, validate) => {
|
|
|
17
17
|
catch {
|
|
18
18
|
throw new CliError("validation_error", `${label} input must be valid JSON`);
|
|
19
19
|
}
|
|
20
|
-
|
|
20
|
+
// A payment keeps its snake_case fields: it is stored and printed as written.
|
|
21
|
+
const decode = (value, depth = 0, verbatim = false) => {
|
|
21
22
|
if (depth > 16)
|
|
22
23
|
throw new CliError("validation_error", `${label} JSON is nested too deeply`);
|
|
23
24
|
if (Array.isArray(value))
|
|
@@ -28,10 +29,10 @@ const parseFindingsJson = (text, label, validate) => {
|
|
|
28
29
|
for (const [key, item] of Object.entries(value)) {
|
|
29
30
|
if (/[A-Z]/.test(key))
|
|
30
31
|
throw new CliError("validation_error", `${label} JSON fields use snake_case`);
|
|
31
|
-
const name = keys[key] ?? key;
|
|
32
|
+
const name = verbatim ? key : (keys[key] ?? key);
|
|
32
33
|
if (Object.hasOwn(output, name))
|
|
33
34
|
throw new CliError("validation_error", `duplicate ${label} field`);
|
|
34
|
-
Object.defineProperty(output, name, { value: decode(item, depth + 1), enumerable: true });
|
|
35
|
+
Object.defineProperty(output, name, { value: decode(item, depth + 1, key === "payment"), enumerable: true });
|
|
35
36
|
}
|
|
36
37
|
return output;
|
|
37
38
|
};
|
|
@@ -309,6 +309,9 @@ export declare const meetingPreparationAnswer: (packet: MeetingPreparation) => i
|
|
|
309
309
|
id: string;
|
|
310
310
|
excerpt: string;
|
|
311
311
|
offset_ms: number | null;
|
|
312
|
+
language?: string | null | undefined;
|
|
313
|
+
line?: number | undefined;
|
|
314
|
+
lines_total?: number | undefined;
|
|
312
315
|
match?: "corrected" | "words" | undefined;
|
|
313
316
|
corrections?: {
|
|
314
317
|
from: string;
|
|
@@ -29,6 +29,9 @@ export declare const mapPreparationProjectEvidence: (section: NonNullable<Meetin
|
|
|
29
29
|
id: string;
|
|
30
30
|
excerpt: string;
|
|
31
31
|
offset_ms: number | null;
|
|
32
|
+
language?: string | null | undefined;
|
|
33
|
+
line?: number | undefined;
|
|
34
|
+
lines_total?: number | undefined;
|
|
32
35
|
match?: "corrected" | "words" | undefined;
|
|
33
36
|
corrections?: {
|
|
34
37
|
from: string;
|
|
@@ -338,6 +338,9 @@ export declare const readMeetingPreparation: (read: MeetingPreparationReader, in
|
|
|
338
338
|
id: string;
|
|
339
339
|
excerpt: string;
|
|
340
340
|
offset_ms: number | null;
|
|
341
|
+
language?: string | null | undefined;
|
|
342
|
+
line?: number | undefined;
|
|
343
|
+
lines_total?: number | undefined;
|
|
341
344
|
match?: "corrected" | "words" | undefined;
|
|
342
345
|
corrections?: {
|
|
343
346
|
from: string;
|
|
@@ -33,6 +33,21 @@ import { stopOnSignal } from "./patience.js";
|
|
|
33
33
|
const ALL_SINCE = "90d";
|
|
34
34
|
/** What a background `--all` job is listed under; no chat argument is spelled like it. */
|
|
35
35
|
const ALL_CHATS = "--all";
|
|
36
|
+
/** Says where the history limits (history_* settings) stopped the read, so an agent does not take it for all. */
|
|
37
|
+
const limitedLine = (result, status) => {
|
|
38
|
+
if ("items" in result) {
|
|
39
|
+
const { groupTooLarge = 0, olderThanWindow = 0 } = result.limited ?? {};
|
|
40
|
+
if (groupTooLarge + olderThanWindow === 0)
|
|
41
|
+
return undefined;
|
|
42
|
+
return (`the history limits (history_* settings) left ${groupTooLarge} big group(s) unread and read ${olderThanWindow} ` +
|
|
43
|
+
`chat(s) only to the window's start — \`${status}\` lists them; \`messages sync <chat> --full-history\` reads one in full`);
|
|
44
|
+
}
|
|
45
|
+
if (result.limited === "group_too_large")
|
|
46
|
+
return "not read: a group above history_group_members_max — `--full-history` reads it and keeps it in full";
|
|
47
|
+
if (result.limited === "older_than_window")
|
|
48
|
+
return "read to the history window's start (history_dialog_days / history_group_days) — `--full-history` reads further";
|
|
49
|
+
return undefined;
|
|
50
|
+
};
|
|
36
51
|
export const syncCommand = (messenger) => {
|
|
37
52
|
const fetching = messenger.fetching ?? FETCHING;
|
|
38
53
|
return annotate(new Command("sync"), {
|
|
@@ -58,7 +73,7 @@ export const syncCommand = (messenger) => {
|
|
|
58
73
|
.option("--catch-up-chunks <n>", "at most this many local vector chunks", wholeNumber)
|
|
59
74
|
.option("--catch-up-messages <n>", "skip a graph rebuild larger than this many messages", wholeNumber)
|
|
60
75
|
.option("--catch-up-time <duration>", "local preparation time budget, 30s by default")
|
|
61
|
-
.option("--full-history", "keep this chat's
|
|
76
|
+
.option("--full-history", "read and keep this chat's history in full from now on, past the history limits (history_* settings)")
|
|
62
77
|
.option("--background", "run as a job that outlives this command; `store jobs show` follows it")
|
|
63
78
|
.option("--dry-run", "only estimate how many messages, requests and minutes a full fetch would still take — from the store, no request")
|
|
64
79
|
.action(async function (chat) {
|
|
@@ -151,6 +166,7 @@ export const syncCommand = (messenger) => {
|
|
|
151
166
|
limit,
|
|
152
167
|
pageSize,
|
|
153
168
|
pauseMs,
|
|
169
|
+
...(include ? { fullHistory: true } : {}),
|
|
154
170
|
catchUp: prepare ? preparation : false,
|
|
155
171
|
...(sinceMs === undefined ? {} : { sinceMs }),
|
|
156
172
|
...(last === undefined ? {} : { last }),
|
|
@@ -191,9 +207,20 @@ export const syncCommand = (messenger) => {
|
|
|
191
207
|
]
|
|
192
208
|
: []),
|
|
193
209
|
...(toReview > 0 ? [nextStep("mentions.review", { args: { new: true }, values: { shown } })] : []),
|
|
210
|
+
...("limited" in result && result.limited && "chat" in result && result.chat
|
|
211
|
+
? [
|
|
212
|
+
{
|
|
213
|
+
...nextStep("messages.sync", { args: { chat: result.chat, full_history: true } }),
|
|
214
|
+
why: "reads this chat past the history limits and keeps it in full from now on",
|
|
215
|
+
},
|
|
216
|
+
]
|
|
217
|
+
: []),
|
|
194
218
|
],
|
|
195
219
|
meta: window.meta,
|
|
196
220
|
}));
|
|
221
|
+
const limitedNote = limitedLine(result, lineOf(this, "store status"));
|
|
222
|
+
if (limitedNote)
|
|
223
|
+
context.renderer.note(limitedNote);
|
|
197
224
|
if (tasks?.held)
|
|
198
225
|
context.renderer.note(`the first sync left ${tasks.held} possible task(s) in ${tasks.heldChats ?? 0} chat(s) unopened ` +
|
|
199
226
|
`(config task_first_pass_per_chat) — ask the owner, then \`${lineOf(this, "tasks scan <chat>")}\``);
|
|
@@ -422,6 +422,7 @@ export const messengerContext = (command, messenger, resolving = {}) => {
|
|
|
422
422
|
// Turning recording off by name (--no-record, or record: false) keeps no search history either.
|
|
423
423
|
history: base.settings.record,
|
|
424
424
|
searchCatchUp: base.settings.searchCatchUp,
|
|
425
|
+
...(base.settings.historyLimits ? { historyLimits: base.settings.historyLimits } : {}),
|
|
425
426
|
pictures: base.settings.pictureSearch,
|
|
426
427
|
reranker: base.settings.searchReranker,
|
|
427
428
|
guard,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { nextStep } from "../steps.js";
|
|
2
2
|
const SHOWN = 5;
|
|
3
3
|
const reasonOf = (skip) => skip.reason === "group_too_large"
|
|
4
|
-
? `a group of ${skip.members ?? "many"} people, not saved`
|
|
4
|
+
? `a group of ${skip.members ?? "many"} people, not ${skip.unread ? "read" : "saved"}`
|
|
5
5
|
: `only from ${skip.keptFrom?.slice(0, 10) ?? "the window's start"}`;
|
|
6
6
|
const nameOf = (skip) => skip.title ?? skip.chatId;
|
|
7
7
|
/**
|
|
@@ -15,7 +15,7 @@ export const historyLimitGaps = (skips) => {
|
|
|
15
15
|
const messages = skips.reduce((sum, skip) => sum + skip.messages, 0);
|
|
16
16
|
const listed = skips
|
|
17
17
|
.slice(0, SHOWN)
|
|
18
|
-
.map((skip) => `${nameOf(skip)} (${skip.messages}, ${reasonOf(skip)})`)
|
|
18
|
+
.map((skip) => `${nameOf(skip)} (${skip.messages > 0 ? `${skip.messages}, ` : ""}${reasonOf(skip)})`)
|
|
19
19
|
.join("; ");
|
|
20
20
|
const more = skips.length > SHOWN ? `; and ${skips.length - SHOWN} more — \`store status\` lists them` : "";
|
|
21
21
|
return {
|
|
@@ -28,8 +28,8 @@ export const historyLimitGaps = (skips) => {
|
|
|
28
28
|
missing: [
|
|
29
29
|
{
|
|
30
30
|
field: "history_limits",
|
|
31
|
-
why: `the history limits (history_* settings) left
|
|
32
|
-
`
|
|
31
|
+
why: `the history limits (history_* settings) left history of ${skips.length} chats out of the store` +
|
|
32
|
+
`${messages > 0 ? ` (${messages} pushed messages counted)` : ""}: ${listed}${more}`,
|
|
33
33
|
step: "messages.sync",
|
|
34
34
|
},
|
|
35
35
|
],
|
|
@@ -355,6 +355,7 @@ const serveProfiles = async (self, context, host, flags, set) => {
|
|
|
355
355
|
tokenFile,
|
|
356
356
|
lockFile: httpLockFile(tokenFile),
|
|
357
357
|
version: app.version,
|
|
358
|
+
reaches: app.messenger === false ? `your ${host.name}` : "your messages",
|
|
358
359
|
onCode: (code, expires) => context.renderer.note(env[mcpHttpMarker(app.envPrefix)] === undefined
|
|
359
360
|
? `login code for a new browser app: ${code} (until ${expires.toTimeString().slice(0, 5)}; a new one after each login)`
|
|
360
361
|
: `a new login code for a browser app: \`${app.command} mcp server status --profiles ${set.option}\``),
|
|
@@ -26,14 +26,13 @@ export const deleteCommand = (messenger) => annotate(new Command("delete"), {
|
|
|
26
26
|
if (environmentOf(this).agent && (everyone || messenger.deleteForMe === false))
|
|
27
27
|
throw new CliError("permission_error", `${everyone ? "" : `${messenger.name ?? messenger.provider} deletes only for everyone, and `}an agent never ` +
|
|
28
28
|
`deletes for everyone — the owner can run \`${lineOf(this, "messages delete --for-everyone")}\``, { permission: "messages.delete", sent: false });
|
|
29
|
+
const ids = messages
|
|
30
|
+
.flatMap((one) => one.split(","))
|
|
31
|
+
.map((one) => one.trim())
|
|
32
|
+
.filter(Boolean);
|
|
33
|
+
if (ids.length === 0)
|
|
34
|
+
throw new CliError("validation_error", "no message id given — name the messages to delete", { sent: false });
|
|
29
35
|
const context = messengerContext(this, messenger);
|
|
30
|
-
const deleted = await context.withServices((services) => services.messages.delete({
|
|
31
|
-
chat,
|
|
32
|
-
messages: messages
|
|
33
|
-
.flatMap((one) => one.split(","))
|
|
34
|
-
.map((one) => one.trim())
|
|
35
|
-
.filter(Boolean),
|
|
36
|
-
forEveryone: everyone,
|
|
37
|
-
}));
|
|
36
|
+
const deleted = await context.withServices((services) => services.messages.delete({ chat, messages: ids, forEveryone: everyone }));
|
|
38
37
|
renderAnswer(this, context, itemOf(snakeKeys(deleted)));
|
|
39
38
|
});
|
|
@@ -3,12 +3,15 @@ import { annotate, annotateOption } from "@wirecat/cli-base/commands";
|
|
|
3
3
|
import { Command, Option } from "commander";
|
|
4
4
|
import { parseDuration } from "../../domain/duration.js";
|
|
5
5
|
import { FETCHING } from "../../services/archive.js";
|
|
6
|
+
import { projectStatus, STATUS_SIGNALS } from "../../services/project-status/index.js";
|
|
6
7
|
import { gatherStoredProject } from "../../services/stored-project-gathering.js";
|
|
7
|
-
import {
|
|
8
|
+
import { answerEntity } from "../answer-entity.js";
|
|
9
|
+
import { FETCH, LOCAL_READ } from "../effects.js";
|
|
8
10
|
import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
|
|
9
11
|
import { positiveCount } from "../paging.js";
|
|
10
12
|
import { projectAnswerParts, projectCutoffTime, projectEvidenceText, projectWindowOf, withSyncCommands, } from "../project-evidence.js";
|
|
11
|
-
import {
|
|
13
|
+
import { projectStatusAnswer, projectStatusRow, projectStatusSchema, projectStatusText } from "../project-status.js";
|
|
14
|
+
import { lineOf, nextStep, rootOf, stepLine } from "../steps.js";
|
|
12
15
|
import { messengerContext } from "./context.js";
|
|
13
16
|
const collect = (value, previous) => [...previous, value];
|
|
14
17
|
const upTo = (most, flag) => (value) => {
|
|
@@ -128,8 +131,50 @@ export const projectsCommand = (messenger) => {
|
|
|
128
131
|
};
|
|
129
132
|
renderAnswer(this, context, itemOf(snakeKeys(withSyncCommands(found, hints.sync)), parts), () => projectEvidenceText(found, hints));
|
|
130
133
|
}));
|
|
134
|
+
projects.addCommand(statusCommand(messenger));
|
|
131
135
|
return projects;
|
|
132
136
|
};
|
|
137
|
+
/** Where a project stands from this profile's stored messages: no model call, nothing leaves the machine. */
|
|
138
|
+
const statusCommand = (messenger) => answerEntity(annotate(new Command("status"), {
|
|
139
|
+
examples: [`${messenger.app.command} personal projects status HRB --context-query 1C`],
|
|
140
|
+
effects: LOCAL_READ,
|
|
141
|
+
partial: true,
|
|
142
|
+
})
|
|
143
|
+
.summary("where a project stands in this account's linked chats: whose move, open asks, money")
|
|
144
|
+
.description("per chat confirmed as the project's (and its child projects'), from what the store holds: the last message, " +
|
|
145
|
+
"who replies next, open questions both ways, promises, amounts with their direction, payments recorded, " +
|
|
146
|
+
"how long each --context-query has been quiet on their side, and how much of the chat is held. Amounts, " +
|
|
147
|
+
"directions and promises found by words are marked pattern: read the cited message before trusting one. " +
|
|
148
|
+
"Only this profile's account is read; the other linked chats are named, and memo projects status reads them")
|
|
149
|
+
.argument("<project>", "an existing project's id, key, exact name or alias")
|
|
150
|
+
.option("--since-time <time>", "how far back: days (60d, at most 730d) or a start date (2026-09-01)", "60d")
|
|
151
|
+
.option("--before-time <time>", "fixed exclusive end: ISO 8601 timestamp with UTC offset")
|
|
152
|
+
.option("--context-query <words>", "words that all appear in one text; reports their last mention per side. Repeat for another", collect, [])
|
|
153
|
+
.action(async function (project) {
|
|
154
|
+
const options = this.opts();
|
|
155
|
+
const context = messengerContext(this, messenger);
|
|
156
|
+
const found = await context.withStore((store, account) => projectStatus(store, {
|
|
157
|
+
project,
|
|
158
|
+
accounts: [account],
|
|
159
|
+
...projectWindowOf(options.sinceTime),
|
|
160
|
+
...(options.beforeTime === undefined ? {} : { asOf: projectCutoffTime(options.beforeTime) }),
|
|
161
|
+
contextQueries: options.contextQuery,
|
|
162
|
+
}));
|
|
163
|
+
const root = rootOf(this);
|
|
164
|
+
const hints = {
|
|
165
|
+
sync: (chat, since) => nextStep("messages.sync", { args: { chat: chat.chatId, since_time: since } }),
|
|
166
|
+
read: (chat, since) => nextStep("messages.list", { args: { chat: chat.chatId, query: `date>=${since}` } }),
|
|
167
|
+
line: (step) => (step.kind === "command" ? lineOf(this, wordsOf(root, step)) : step.why),
|
|
168
|
+
};
|
|
169
|
+
const { item, ...parts } = projectStatusAnswer(found, hints);
|
|
170
|
+
renderAnswer(this, context, itemOf(item, parts), () => projectStatusText(found, hints));
|
|
171
|
+
}), "project_status", projectStatusSchema, { row: projectStatusRow(STATUS_SIGNALS) });
|
|
172
|
+
/** A step's words after the tool and the profile, so `lineOf` names the profile the run used. */
|
|
173
|
+
const wordsOf = (root, step) => {
|
|
174
|
+
const profile = root.getOptionValue("profile");
|
|
175
|
+
const typed = typeof profile === "string" && profile !== "" ? 2 : 1;
|
|
176
|
+
return stepLine(root, step).split(" ").slice(typed).join(" ");
|
|
177
|
+
};
|
|
133
178
|
const quote = (text) => (/^[\w:.-]+$/.test(text) ? text : `"${text.replace(/(["\\$`])/g, "\\$1")}"`);
|
|
134
179
|
/** A line per saved page, counts only; a timed heartbeat with titles is the sync-progress work's (cli-core#2121). */
|
|
135
180
|
const progressNote = (note) => ({ chat, index, of, fetched }) => note(`chat ${index + 1} of ${of} · ${chat} · ${fetched} messages so far`);
|
|
@@ -293,7 +293,14 @@ export const allHitItem = (item) => item.kind === "meeting"
|
|
|
293
293
|
offset_ms: item.startMs,
|
|
294
294
|
scope: item.scope,
|
|
295
295
|
id: String(item.id),
|
|
296
|
-
...(item.cueRef === undefined
|
|
296
|
+
...(item.cueRef === undefined
|
|
297
|
+
? {}
|
|
298
|
+
: {
|
|
299
|
+
cue_ref: item.cueRef,
|
|
300
|
+
sender_name: item.speakerName ?? null,
|
|
301
|
+
language: item.language ?? null,
|
|
302
|
+
...(item.line === undefined ? {} : { line: item.line, lines_total: item.linesTotal }),
|
|
303
|
+
}),
|
|
297
304
|
untrusted: ALL_HIT_UNTRUSTED.meeting,
|
|
298
305
|
}
|
|
299
306
|
: defined({
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Chat } from "../../domain/models.js";
|
|
2
|
+
import type { ServeNotice } from "./serve-notice.js";
|
|
3
|
+
/** The provider's own catch-up lands first; this pass then finds most chats already current. */
|
|
4
|
+
export declare const START_SYNC_DELAY_MS = 30000;
|
|
5
|
+
export declare const START_SYNC_BUDGET_MS: number;
|
|
6
|
+
/** A longer downtime is `messages sync --all`'s job, not a start's. */
|
|
7
|
+
export declare const START_SYNC_WINDOW_MS: number;
|
|
8
|
+
export declare const START_SYNC_PER_CHAT = 500;
|
|
9
|
+
export interface StartSyncSummary {
|
|
10
|
+
chats: number;
|
|
11
|
+
fetched: number;
|
|
12
|
+
failed: number;
|
|
13
|
+
stopped: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface StartSync {
|
|
16
|
+
/** Runs the pass once, after the delay; a second call is ignored. */
|
|
17
|
+
start(): void;
|
|
18
|
+
/** Aborts the pass and waits for it, which stops before its next request. */
|
|
19
|
+
stop(): Promise<void>;
|
|
20
|
+
summary(): StartSyncSummary | undefined;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* `serve`'s one pass on start over the chats that moved while nothing listened. A provider's own
|
|
24
|
+
* catch-up drops a gap it finds too long (Telegram's `differenceTooLong`, `channelDifferenceTooLong`
|
|
25
|
+
* past 100 updates), so each chat whose newest message is newer than the newest one held is read back
|
|
26
|
+
* to that one. A chat never held is read back to the newest message held anywhere, which is about when
|
|
27
|
+
* the last serve stopped. Bounded by a window, a per-chat limit and a time budget; an empty store is
|
|
28
|
+
* left to `messages sync --all`.
|
|
29
|
+
*/
|
|
30
|
+
export declare const startSync: ({ chats, newestHeld, fetch, note, warn, delayMs, budgetMs, windowMs, now, }: {
|
|
31
|
+
chats: (stop: AbortSignal) => Promise<Chat[]>;
|
|
32
|
+
/** The newest message held from before this serve started, epoch milliseconds. */
|
|
33
|
+
newestHeld: (chatId: string) => Promise<number | undefined>;
|
|
34
|
+
fetch: (chatId: string, sinceMs: number, stop: AbortSignal) => Promise<{
|
|
35
|
+
fetched: number;
|
|
36
|
+
}>;
|
|
37
|
+
note: (notice: ServeNotice) => void;
|
|
38
|
+
warn: (notice: ServeNotice) => void;
|
|
39
|
+
delayMs?: number;
|
|
40
|
+
budgetMs?: number;
|
|
41
|
+
windowMs?: number;
|
|
42
|
+
now?: () => number;
|
|
43
|
+
}) => StartSync;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { failureKey } from "../../errors/failures.js";
|
|
2
|
+
/** The provider's own catch-up lands first; this pass then finds most chats already current. */
|
|
3
|
+
export const START_SYNC_DELAY_MS = 30_000;
|
|
4
|
+
export const START_SYNC_BUDGET_MS = 5 * 60_000;
|
|
5
|
+
/** A longer downtime is `messages sync --all`'s job, not a start's. */
|
|
6
|
+
export const START_SYNC_WINDOW_MS = 7 * 86_400_000;
|
|
7
|
+
export const START_SYNC_PER_CHAT = 500;
|
|
8
|
+
/**
|
|
9
|
+
* `serve`'s one pass on start over the chats that moved while nothing listened. A provider's own
|
|
10
|
+
* catch-up drops a gap it finds too long (Telegram's `differenceTooLong`, `channelDifferenceTooLong`
|
|
11
|
+
* past 100 updates), so each chat whose newest message is newer than the newest one held is read back
|
|
12
|
+
* to that one. A chat never held is read back to the newest message held anywhere, which is about when
|
|
13
|
+
* the last serve stopped. Bounded by a window, a per-chat limit and a time budget; an empty store is
|
|
14
|
+
* left to `messages sync --all`.
|
|
15
|
+
*/
|
|
16
|
+
export const startSync = ({ chats, newestHeld, fetch, note, warn, delayMs = START_SYNC_DELAY_MS, budgetMs = START_SYNC_BUDGET_MS, windowMs = START_SYNC_WINDOW_MS, now = Date.now, }) => {
|
|
17
|
+
const controller = new AbortController();
|
|
18
|
+
let timer;
|
|
19
|
+
let pass = Promise.resolve();
|
|
20
|
+
let started = false;
|
|
21
|
+
let done;
|
|
22
|
+
const run = async () => {
|
|
23
|
+
const stop = AbortSignal.any([controller.signal, AbortSignal.timeout(budgetMs)]);
|
|
24
|
+
const floor = now() - windowMs;
|
|
25
|
+
const moved = [];
|
|
26
|
+
for (const chat of await chats(stop)) {
|
|
27
|
+
const lastAt = Date.parse(chat.lastMessageAt ?? "");
|
|
28
|
+
if (Number.isNaN(lastAt) || lastAt < floor)
|
|
29
|
+
continue;
|
|
30
|
+
moved.push({ chat, lastAt, held: await newestHeld(chat.id) });
|
|
31
|
+
}
|
|
32
|
+
const anywhere = moved.reduce((top, { held }) => (held === undefined ? top : Math.max(top, held)), -Infinity);
|
|
33
|
+
if (!Number.isFinite(anywhere))
|
|
34
|
+
return;
|
|
35
|
+
const behind = moved
|
|
36
|
+
.filter(({ lastAt, held }) => lastAt > (held ?? anywhere))
|
|
37
|
+
.sort((one, other) => other.lastAt - one.lastAt);
|
|
38
|
+
if (behind.length === 0)
|
|
39
|
+
return;
|
|
40
|
+
const summary = { chats: 0, fetched: 0, failed: 0, stopped: false };
|
|
41
|
+
done = summary;
|
|
42
|
+
for (const { chat, held } of behind) {
|
|
43
|
+
if (stop.aborted)
|
|
44
|
+
break;
|
|
45
|
+
try {
|
|
46
|
+
summary.fetched += (await fetch(chat.id, Math.max(held ?? anywhere, floor), stop)).fetched;
|
|
47
|
+
summary.chats += 1;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (stop.aborted)
|
|
51
|
+
break;
|
|
52
|
+
summary.failed += 1;
|
|
53
|
+
const errorCode = failureKey(error);
|
|
54
|
+
warn({
|
|
55
|
+
code: "start_sync_chat_failed",
|
|
56
|
+
chatId: chat.id,
|
|
57
|
+
errorCode,
|
|
58
|
+
text: `what ${chat.id} received while serve was down was not fetched: ${errorCode}`,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
summary.stopped = stop.aborted && summary.chats + summary.failed < behind.length;
|
|
63
|
+
note({
|
|
64
|
+
code: "start_sync",
|
|
65
|
+
text: `caught up ${summary.fetched} message(s) in ${summary.chats} chat(s) that moved while serve was down` +
|
|
66
|
+
(summary.stopped ? ` — stopped at the time limit, ${behind.length - summary.chats - summary.failed} left` : ""),
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
return {
|
|
70
|
+
start: () => {
|
|
71
|
+
if (started || controller.signal.aborted)
|
|
72
|
+
return;
|
|
73
|
+
started = true;
|
|
74
|
+
timer = setTimeout(() => {
|
|
75
|
+
pass = run().catch((error) => {
|
|
76
|
+
if (controller.signal.aborted)
|
|
77
|
+
return;
|
|
78
|
+
const errorCode = failureKey(error);
|
|
79
|
+
warn({ code: "start_sync_failed", errorCode, text: `the catch-up on start failed: ${errorCode}` });
|
|
80
|
+
});
|
|
81
|
+
}, delayMs);
|
|
82
|
+
},
|
|
83
|
+
stop: async () => {
|
|
84
|
+
controller.abort();
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
await pass;
|
|
87
|
+
},
|
|
88
|
+
summary: () => done,
|
|
89
|
+
};
|
|
90
|
+
};
|