@wirecat/cli-core 0.399.0 → 0.401.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 +111 -0
- package/README.md +1 -1
- package/dist/analysis/consents.d.ts +11 -6
- package/dist/analysis/consents.js +5 -2
- package/dist/cli/bot/mcp.js +2 -1
- package/dist/cli/bot/store.js +1 -1
- package/dist/cli/config-command.js +20 -1
- package/dist/cli/context.d.ts +2 -0
- package/dist/cli/context.js +1 -1
- package/dist/cli/effects.d.ts +4 -1
- package/dist/cli/effects.js +10 -2
- package/dist/cli/index.d.ts +3 -2
- package/dist/cli/index.js +3 -2
- package/dist/cli/json-fields.js +12 -1
- package/dist/cli/meeting-evidence-answer.d.ts +46 -0
- package/dist/cli/meeting-evidence-answer.js +22 -0
- package/dist/cli/meeting-preparation-answer.js +1 -13
- package/dist/cli/messenger/account-command.js +15 -6
- package/dist/cli/messenger/account-profile-query.d.ts +2 -0
- package/dist/cli/messenger/account-profile-query.js +13 -0
- package/dist/cli/messenger/account-sessions-command.js +1 -2
- package/dist/cli/messenger/admin-commands.js +12 -12
- package/dist/cli/messenger/admin-contacts-command.js +6 -3
- package/dist/cli/messenger/admin-folders-command.js +5 -5
- package/dist/cli/messenger/admin-moderation-command.js +4 -4
- package/dist/cli/messenger/admin-statistics-command.js +6 -14
- package/dist/cli/messenger/analysis-consents-query.d.ts +5 -0
- package/dist/cli/messenger/analysis-consents-query.js +20 -0
- package/dist/cli/messenger/archive-commands.js +3 -3
- package/dist/cli/messenger/attachments-command.js +3 -0
- package/dist/cli/messenger/backfill-command.js +5 -4
- package/dist/cli/messenger/chat-queries.d.ts +1 -0
- package/dist/cli/messenger/chat-queries.js +12 -2
- package/dist/cli/messenger/chats-command.js +2 -2
- package/dist/cli/messenger/chats-documents-command.js +7 -5
- package/dist/cli/messenger/chats-members-command.js +7 -3
- package/dist/cli/messenger/chats-read-command.js +1 -1
- package/dist/cli/messenger/chats-tracking-command.js +14 -6
- package/dist/cli/messenger/contacts-command.js +9 -5
- package/dist/cli/messenger/conversations-command.js +20 -16
- package/dist/cli/messenger/doctor-command.js +4 -1
- package/dist/cli/messenger/document-links-query.d.ts +2 -0
- package/dist/cli/messenger/document-links-query.js +9 -0
- package/dist/cli/messenger/download-command.js +1 -0
- package/dist/cli/messenger/flood-command.d.ts +2 -2
- package/dist/cli/messenger/flood-command.js +3 -3
- package/dist/cli/messenger/gaps-command.js +2 -2
- package/dist/cli/messenger/guard-commands.js +35 -13
- package/dist/cli/messenger/images-command.js +1 -0
- package/dist/cli/messenger/media-archive-command.js +2 -0
- package/dist/cli/messenger/media-archive-move-command.js +1 -0
- package/dist/cli/messenger/media-archive-timer-command.js +1 -1
- package/dist/cli/messenger/mentions-command.js +15 -2
- package/dist/cli/messenger/message-writes.d.ts +192 -2
- package/dist/cli/messenger/message-writes.js +6 -2
- package/dist/cli/messenger/messages-command.js +2 -0
- package/dist/cli/messenger/messages-delete-command.js +1 -0
- package/dist/cli/messenger/messages-edit-command.js +1 -0
- package/dist/cli/messenger/messages-evidence-command.js +3 -1
- package/dist/cli/messenger/messages-forward-command.js +1 -0
- package/dist/cli/messenger/messages-pin-command.js +3 -0
- package/dist/cli/messenger/messages-send-command.js +10 -6
- package/dist/cli/messenger/metadata-command.js +6 -1
- package/dist/cli/messenger/models-command.js +10 -5
- package/dist/cli/messenger/polls-command.js +4 -0
- package/dist/cli/messenger/private-people-command.js +3 -3
- package/dist/cli/messenger/rankings-command.js +6 -11
- package/dist/cli/messenger/reactions-command.js +2 -0
- package/dist/cli/messenger/recipients-query.d.ts +5 -0
- package/dist/cli/messenger/recipients-query.js +18 -0
- package/dist/cli/messenger/records-command.js +13 -8
- package/dist/cli/messenger/review.js +1 -1
- package/dist/cli/messenger/schedule-command.js +2 -2
- package/dist/cli/messenger/search-answers.js +2 -0
- package/dist/cli/messenger/search-command.js +3 -1
- package/dist/cli/messenger/searches-command.js +5 -1
- package/dist/cli/messenger/sends-query.d.ts +5 -0
- package/dist/cli/messenger/sends-query.js +33 -0
- package/dist/cli/messenger/serve-command.js +1 -0
- package/dist/cli/messenger/server-command.js +0 -3
- package/dist/cli/messenger/statistics-query.d.ts +14 -0
- package/dist/cli/messenger/statistics-query.js +143 -0
- package/dist/cli/messenger/stats-command.js +5 -5
- package/dist/cli/messenger/store-maintenance-command.js +10 -10
- package/dist/cli/messenger/stored.js +4 -1
- package/dist/cli/messenger/tags-command.js +7 -1
- package/dist/cli/messenger/tasks-command.js +57 -14
- package/dist/cli/messenger/transcribe-command.js +1 -0
- package/dist/cli/messenger/watch-answers.d.ts +6 -0
- package/dist/cli/messenger/watch-answers.js +69 -0
- package/dist/cli/messenger/watch-command.d.ts +1 -1
- package/dist/cli/messenger/watch-command.js +56 -7
- package/dist/cli/program.js +5 -1
- package/dist/cli/runs/command.js +25 -38
- package/dist/cli/runs/query.d.ts +5 -0
- package/dist/cli/runs/query.js +32 -0
- package/dist/cli/runs/search-command.d.ts +4 -0
- package/dist/cli/runs/search-command.js +27 -0
- package/dist/cli/runs/search-query.d.ts +4 -0
- package/dist/cli/runs/search-query.js +50 -0
- package/dist/cli/runs/search.d.ts +2 -1
- package/dist/cli/runs/search.js +6 -6
- package/dist/documents/index.d.ts +1 -0
- package/dist/documents/index.js +1 -0
- package/dist/domain/attachments.js +9 -1
- package/dist/domain/rankings-options.js +3 -3
- package/dist/entities/account-change.d.ts +89 -0
- package/dist/entities/account-change.js +20 -0
- package/dist/entities/account-profile.d.ts +7 -0
- package/dist/entities/account-profile.js +8 -0
- package/dist/entities/analysis-consent.d.ts +9 -0
- package/dist/entities/analysis-consent.js +4 -0
- package/dist/entities/attachment-results.d.ts +96 -0
- package/dist/entities/attachment-results.js +31 -0
- package/dist/entities/batch-result.d.ts +27 -0
- package/dist/entities/batch-result.js +18 -0
- package/dist/entities/chat-admin.d.ts +234 -0
- package/dist/entities/chat-admin.js +84 -0
- package/dist/entities/chat-change.d.ts +25 -0
- package/dist/entities/chat-change.js +11 -0
- package/dist/entities/chat-metadata.d.ts +34 -0
- package/dist/entities/chat-metadata.js +15 -0
- package/dist/entities/chat-moderation.d.ts +44 -0
- package/dist/entities/chat-moderation.js +37 -0
- package/dist/entities/chat-tracking.d.ts +26 -0
- package/dist/entities/chat-tracking.js +23 -0
- package/dist/entities/contact-activity.d.ts +52 -0
- package/dist/entities/contact-activity.js +42 -0
- package/dist/entities/contact-context.d.ts +1568 -0
- package/dist/entities/contact-context.js +191 -0
- package/dist/entities/contact-write.d.ts +22 -0
- package/dist/entities/contact-write.js +6 -0
- package/dist/entities/conversation-preparation.d.ts +63 -0
- package/dist/entities/conversation-preparation.js +23 -0
- package/dist/entities/conversation-results.d.ts +567 -0
- package/dist/entities/conversation-results.js +94 -0
- package/dist/entities/document-link.d.ts +13 -0
- package/dist/entities/document-link.js +11 -0
- package/dist/entities/extraction-report.d.ts +95 -0
- package/dist/entities/extraction-report.js +34 -0
- package/dist/entities/hit.d.ts +4 -0
- package/dist/entities/hit.js +4 -0
- package/dist/entities/image-embedding.d.ts +9 -0
- package/dist/entities/image-embedding.js +10 -0
- package/dist/entities/index.d.ts +8284 -1921
- package/dist/entities/index.js +178 -3
- package/dist/entities/listener.d.ts +2150 -0
- package/dist/entities/listener.js +46 -0
- package/dist/entities/media-archive.d.ts +43 -0
- package/dist/entities/media-archive.js +32 -0
- package/dist/entities/meeting-evidence.d.ts +86 -0
- package/dist/entities/meeting-evidence.js +25 -0
- package/dist/entities/meeting-preparation-parts.d.ts +14 -0
- package/dist/entities/meeting-preparation-parts.js +15 -13
- package/dist/entities/meeting-task-proposal.d.ts +14 -0
- package/dist/entities/meeting-task-proposal.js +19 -0
- package/dist/entities/meeting.d.ts +1 -1
- package/dist/entities/meeting.js +1 -1
- package/dist/entities/mention.d.ts +63 -0
- package/dist/entities/mention.js +44 -0
- package/dist/entities/message-change.d.ts +19 -0
- package/dist/entities/message-change.js +6 -0
- package/dist/entities/message-context.d.ts +617 -0
- package/dist/entities/message-context.js +20 -0
- package/dist/entities/message-read.d.ts +73 -0
- package/dist/entities/message-read.js +58 -0
- package/dist/entities/message-transfer.d.ts +379 -0
- package/dist/entities/message-transfer.js +83 -0
- package/dist/entities/message-write.d.ts +378 -0
- package/dist/entities/message-write.js +10 -0
- package/dist/entities/model-key.d.ts +14 -0
- package/dist/entities/model-key.js +10 -0
- package/dist/entities/poll.d.ts +40 -0
- package/dist/entities/poll.js +16 -0
- package/dist/entities/private-contact.d.ts +10 -0
- package/dist/entities/private-contact.js +4 -0
- package/dist/entities/reaction-change.d.ts +7 -0
- package/dist/entities/reaction-change.js +8 -0
- package/dist/entities/recipient.d.ts +29 -0
- package/dist/entities/recipient.js +11 -0
- package/dist/entities/recorded-run.d.ts +103 -0
- package/dist/entities/recorded-run.js +44 -0
- package/dist/entities/saved-search.d.ts +4 -0
- package/dist/entities/saved-search.js +2 -0
- package/dist/entities/send-attempt.d.ts +31 -0
- package/dist/entities/send-attempt.js +29 -0
- package/dist/entities/store-maintenance.d.ts +319 -0
- package/dist/entities/store-maintenance.js +145 -0
- package/dist/entities/store-records.d.ts +442 -0
- package/dist/entities/store-records.js +99 -0
- package/dist/entities/tag-change.d.ts +74 -0
- package/dist/entities/tag-change.js +31 -0
- package/dist/entities/task-pair.d.ts +31 -0
- package/dist/entities/task-pair.js +23 -0
- package/dist/entities/task-results.d.ts +524 -0
- package/dist/entities/task-results.js +142 -0
- package/dist/errors/reasons.js +3 -0
- package/dist/mcp/agent.d.ts +3 -1
- package/dist/mcp/agent.js +17 -10
- package/dist/mcp/bot/server.d.ts +2 -0
- package/dist/mcp/bot/server.js +1 -0
- package/dist/mcp/server.js +3 -1
- package/dist/mcp/tools/attachments.js +1 -0
- package/dist/meetings/cli/index.js +4 -2
- package/dist/meetings/cli/passage.js +1 -0
- package/dist/meetings/index.d.ts +2 -1
- package/dist/meetings/index.js +1 -0
- package/dist/meetings/links.d.ts +16 -0
- package/dist/meetings/read-answers.d.ts +185 -0
- package/dist/meetings/read-answers.js +213 -0
- package/dist/ports/messenger.d.ts +10 -1
- package/dist/runs/run.d.ts +10 -2
- package/dist/runs/run.js +11 -3
- package/dist/sends/guard.d.ts +1 -1
- package/dist/sends/guard.js +9 -5
- package/dist/sends/journal.d.ts +9 -2
- package/dist/sends/journal.js +9 -2
- package/dist/sends/permissions.d.ts +5 -1
- package/dist/sends/permissions.js +42 -2
- package/dist/sends/recipients.d.ts +8 -0
- package/dist/sends/recipients.js +9 -0
- package/dist/sends/upload.d.ts +4 -4
- package/dist/sends/upload.js +24 -20
- package/dist/services/admin-statistics.js +3 -3
- package/dist/services/chat-documents.d.ts +1 -0
- package/dist/services/chat-documents.js +9 -3
- package/dist/services/chats.d.ts +1 -1
- package/dist/services/chats.js +5 -2
- package/dist/services/documents/mirrors.d.ts +98 -0
- package/dist/services/documents/mirrors.js +144 -0
- package/dist/services/meeting-links.d.ts +2 -17
- package/dist/services/messages.js +1 -0
- package/dist/services/search-all-meetings.js +21 -8
- package/dist/services/task-rules.d.ts +1 -1
- package/dist/services/tasks.d.ts +2 -2
- package/dist/services/tasks.js +1 -1
- package/dist/store/exports.d.ts +1 -1
- package/dist/store/exports.js +1 -1
- package/dist/store/messages.d.ts +3 -1
- package/dist/store/messages.js +2 -2
- package/dist/store/roster.d.ts +4 -1
- package/dist/store/roster.js +2 -2
- package/dist/store/sqlite/chats.d.ts +12 -0
- package/dist/store/sqlite/chats.js +1 -0
- package/dist/store/sqlite/document-links.d.ts +1 -1
- package/dist/store/sqlite/document-links.js +5 -5
- package/dist/store/sqlite/drizzle/bun.js +3 -3
- package/dist/store/sqlite/drizzle/bun.js.map +1 -1
- package/dist/store/sqlite/drizzle/core.js +2 -2
- package/dist/store/sqlite/drizzle/core.js.map +1 -1
- package/dist/store/sqlite/drizzle/node.js +3 -3
- package/dist/store/sqlite/drizzle/node.js.map +1 -1
- package/dist/store/sqlite/drizzle/{shared-O7CTKP45.js → shared-HKXF6HKX.js} +57 -57
- package/dist/store/sqlite/drizzle/{shared-O7CTKP45.js.map → shared-HKXF6HKX.js.map} +1 -1
- package/dist/store/sqlite/involvements.js +2 -1
- package/dist/store/sqlite/roster.d.ts +2 -1
- package/dist/store/sqlite/roster.js +3 -2
- package/package.json +2 -2
- package/parity.json +14 -69
|
@@ -83,8 +83,8 @@ export const callsCommand = (messenger) => {
|
|
|
83
83
|
/** `chats delete` and `chats clear` — for this account only; at the default level they ask first. */
|
|
84
84
|
export const chatDeletionCommands = (messenger) => [
|
|
85
85
|
annotate(new Command("delete"), {
|
|
86
|
+
output: { entity: "chat_removal" },
|
|
86
87
|
effects: REMOTE_WRITE,
|
|
87
|
-
mcp: false,
|
|
88
88
|
examples: [`${messenger.app.command} personal chats delete "Book club"`],
|
|
89
89
|
})
|
|
90
90
|
.description("delete a chat from this account; the others in it keep it and its messages")
|
|
@@ -95,8 +95,8 @@ export const chatDeletionCommands = (messenger) => [
|
|
|
95
95
|
renderAnswer(this, context, itemOf(snakeKeys(await context.withServices((services) => services.admin.deleteChat(chat)))));
|
|
96
96
|
}),
|
|
97
97
|
annotate(new Command("clear"), {
|
|
98
|
+
output: { entity: "chat_removal" },
|
|
98
99
|
effects: REMOTE_WRITE,
|
|
99
|
-
mcp: false,
|
|
100
100
|
examples: [`${messenger.app.command} personal chats clear "Book club"`],
|
|
101
101
|
})
|
|
102
102
|
.description("delete every message in a chat for this account; the others in it keep theirs")
|
|
@@ -110,6 +110,7 @@ export const chatDeletionCommands = (messenger) => [
|
|
|
110
110
|
/** `chats mute` and `unmute` — the owner's own notifications for one chat; nobody in it is told. */
|
|
111
111
|
export const muteCommands = (messenger) => [
|
|
112
112
|
annotate(new Command("mute"), {
|
|
113
|
+
output: { entity: "chat_muting" },
|
|
113
114
|
effects: REMOTE_WRITE,
|
|
114
115
|
examples: [
|
|
115
116
|
`${messenger.app.command} chats mute "Book club"`,
|
|
@@ -126,6 +127,7 @@ export const muteCommands = (messenger) => [
|
|
|
126
127
|
renderAnswer(this, context, itemOf(snakeKeys(await context.withServices((services) => services.account.mute(chat, when)))));
|
|
127
128
|
}),
|
|
128
129
|
annotate(new Command("unmute"), {
|
|
130
|
+
output: { entity: "chat_muting" },
|
|
129
131
|
effects: REMOTE_WRITE,
|
|
130
132
|
examples: [`${messenger.app.command} chats unmute "Book club"`],
|
|
131
133
|
})
|
|
@@ -136,7 +138,8 @@ export const muteCommands = (messenger) => [
|
|
|
136
138
|
renderAnswer(this, context, itemOf(snakeKeys(await context.withServices((services) => services.account.mute(chat, null)))));
|
|
137
139
|
}),
|
|
138
140
|
];
|
|
139
|
-
const onChatCommand = (messenger, name, description, change) => annotate(new Command(name), {
|
|
141
|
+
const onChatCommand = (messenger, name, description, entity, change) => annotate(new Command(name), {
|
|
142
|
+
output: { entity },
|
|
140
143
|
effects: REMOTE_WRITE,
|
|
141
144
|
examples: [`${messenger.app.command} chats ${name} 123456789`],
|
|
142
145
|
})
|
|
@@ -148,16 +151,16 @@ const onChatCommand = (messenger, name, description, change) => annotate(new Com
|
|
|
148
151
|
});
|
|
149
152
|
/** `chats archive` and `unarchive` — the messenger's archive of chats, not the local store. */
|
|
150
153
|
export const archiveCommands = (messenger) => [
|
|
151
|
-
onChatCommand(messenger, "archive", "move a chat into the archive; nobody in it is told", (services, chat) => services.account.archive(chat, true)),
|
|
152
|
-
onChatCommand(messenger, "unarchive", "bring an archived chat back to the chat list", (services, chat) => services.account.archive(chat, false)),
|
|
154
|
+
onChatCommand(messenger, "archive", "move a chat into the archive; nobody in it is told", "chat_archive", (services, chat) => services.account.archive(chat, true)),
|
|
155
|
+
onChatCommand(messenger, "unarchive", "bring an archived chat back to the chat list", "chat_archive", (services, chat) => services.account.archive(chat, false)),
|
|
153
156
|
];
|
|
154
157
|
/** `chats pin` and `unpin` — where the chat sits in the owner's list; `messages pin` pins a message for everyone. */
|
|
155
158
|
export const chatPinCommands = (messenger) => [
|
|
156
|
-
onChatCommand(messenger, "pin", "keep a chat at the top of your chat list; nobody in it is told", (services, chat) => services.account.pinChat(chat, true)),
|
|
157
|
-
onChatCommand(messenger, "unpin", "let a pinned chat move down the chat list again", (services, chat) => services.account.pinChat(chat, false)),
|
|
159
|
+
onChatCommand(messenger, "pin", "keep a chat at the top of your chat list; nobody in it is told", "chat_pin", (services, chat) => services.account.pinChat(chat, true)),
|
|
160
|
+
onChatCommand(messenger, "unpin", "let a pinned chat move down the chat list again", "chat_pin", (services, chat) => services.account.pinChat(chat, false)),
|
|
158
161
|
];
|
|
159
162
|
/** `chats mark-unread` — undone by `chats mark-read`, which the other side does see. */
|
|
160
|
-
export const markUnreadCommand = (messenger) => onChatCommand(messenger, "mark-unread", "mark a chat unread on your own devices; nobody in it is told", (services, chat) => services.account.markUnread(chat));
|
|
163
|
+
export const markUnreadCommand = (messenger) => onChatCommand(messenger, "mark-unread", "mark a chat unread on your own devices; nobody in it is told", "chat_unread", (services, chat) => services.account.markUnread(chat));
|
|
161
164
|
const AUDIENCES = ["everyone", "contacts", "nobody"];
|
|
162
165
|
const audienceOf = (flag, value) => {
|
|
163
166
|
if (!AUDIENCES.includes(value))
|
|
@@ -202,6 +205,7 @@ export const privacyCommand = (messenger) => {
|
|
|
202
205
|
const keys = messenger.privacySettings ?? PRIVACY_SETTINGS;
|
|
203
206
|
const privacy = new Command("privacy").description("who may find, call or add the account");
|
|
204
207
|
annotate(privacy.command("show"), {
|
|
208
|
+
output: { entity: "account_privacy" },
|
|
205
209
|
effects: REMOTE_READ,
|
|
206
210
|
examples: [`${tool} account privacy show`, `${tool} account privacy show --json`],
|
|
207
211
|
})
|
|
@@ -214,6 +218,7 @@ export const privacyCommand = (messenger) => {
|
|
|
214
218
|
const audience = keys.find((key) => !SWITCHES.has(key));
|
|
215
219
|
const toggle = keys.find((key) => SWITCHES.has(key));
|
|
216
220
|
const set = annotate(new Command("set"), {
|
|
221
|
+
output: { entity: "account_privacy_change" },
|
|
217
222
|
effects: REMOTE_WRITE,
|
|
218
223
|
examples: [audience, toggle].flatMap((key) => (key === undefined ? [] : [exampleOf(tool, key)])),
|
|
219
224
|
}).description("change who may find, call or add the account; the settings not named stay");
|
|
@@ -113,7 +113,7 @@ const notes = (found, pretty, renderer) => {
|
|
|
113
113
|
renderer.note(`${chat.title ?? chat.id}: its admins are not known, so only your answers count`);
|
|
114
114
|
if (found.tasks && found.tasks.added + found.tasks.closed > 0)
|
|
115
115
|
renderer.note(`tasks: ${found.tasks.added} opened, ${found.tasks.closed} closed by the rules` +
|
|
116
|
-
(found.tasks.review > 0 ? `, ${found.tasks.review} pair(s) to review — tasks duplicates` : ""));
|
|
116
|
+
(found.tasks.review > 0 ? `, ${found.tasks.review} pair(s) to review — tasks duplicates list` : ""));
|
|
117
117
|
if (found.partial && pretty)
|
|
118
118
|
renderer.warn(PARTIAL_NOTE);
|
|
119
119
|
if (found.offline && pretty)
|
|
@@ -192,7 +192,7 @@ export const scheduleCommand = (messenger) => {
|
|
|
192
192
|
.join("\n");
|
|
193
193
|
});
|
|
194
194
|
});
|
|
195
|
-
annotate(command.command("run"), { effects: REMOTE_WRITE
|
|
195
|
+
annotate(command.command("run"), { effects: REMOTE_WRITE })
|
|
196
196
|
.description("run a schedule now, as its timer would, and wait for it")
|
|
197
197
|
.argument("<name>")
|
|
198
198
|
.action(async function (name) {
|
|
@@ -216,7 +216,7 @@ export const scheduleCommand = (messenger) => {
|
|
|
216
216
|
rmSync(file, { force: true });
|
|
217
217
|
say({ name, removed: true }, [`Removed ${name}.`]);
|
|
218
218
|
});
|
|
219
|
-
annotate(command.command("exec"), { effects: REMOTE_WRITE
|
|
219
|
+
annotate(command.command("exec"), { effects: REMOTE_WRITE })
|
|
220
220
|
.description("what a timer runs, given the schedule's file; to run one by hand, use run")
|
|
221
221
|
.argument("<file>")
|
|
222
222
|
.action(async function (file) {
|
|
@@ -6,6 +6,7 @@ import { searchNotesOf } from "../../services/notes-search.js";
|
|
|
6
6
|
import { LOCAL_READ, REMOTE_READ } from "../effects.js";
|
|
7
7
|
import { renderAnswer } from "../envelope.js";
|
|
8
8
|
import { positiveCount } from "../paging.js";
|
|
9
|
+
import { runsSearchCommand } from "../runs/search-command.js";
|
|
9
10
|
import { commandAt } from "../steps.js";
|
|
10
11
|
import { messengerContext } from "./context.js";
|
|
11
12
|
import { conversationsSearchCommand } from "./conversations-command.js";
|
|
@@ -159,7 +160,8 @@ export const searchCommand = (messenger, { topics = false } = {}) => {
|
|
|
159
160
|
.addCommand(allCommand(messenger))
|
|
160
161
|
.addCommand(messagesSearchCommand(messenger))
|
|
161
162
|
.addCommand(notesCommand(messenger))
|
|
162
|
-
.addCommand(conversationsSearchCommand(messenger))
|
|
163
|
+
.addCommand(conversationsSearchCommand(messenger))
|
|
164
|
+
.addCommand(runsSearchCommand(messenger.app));
|
|
163
165
|
if (topics)
|
|
164
166
|
search.addCommand(topicsSearchCommand(messenger));
|
|
165
167
|
for (const [path, args] of Object.entries({
|
|
@@ -124,7 +124,11 @@ export const searchesCommand = (messenger) => {
|
|
|
124
124
|
const deleted = await context.withServices((services) => services.searches.delete(reference));
|
|
125
125
|
renderAnswer(this, context, itemOf(savedSearchItem(deleted)), () => `deleted ${deleted.name ?? `run ${deleted.id}`}\n`);
|
|
126
126
|
});
|
|
127
|
-
annotate(searches.command("clear"), {
|
|
127
|
+
annotate(searches.command("clear"), {
|
|
128
|
+
output: { entity: "search_history_clear" },
|
|
129
|
+
effects: LOCAL_WRITE,
|
|
130
|
+
examples: [`${tool} searches clear`],
|
|
131
|
+
})
|
|
128
132
|
.description("empty the history; saved searches stay")
|
|
129
133
|
.action(async function () {
|
|
130
134
|
const context = messengerContext(this, messenger);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type QueryRegistry } from "@wirecat/cli-base/query";
|
|
2
|
+
import type { SendFilter } from "../../sends/journal.js";
|
|
3
|
+
import { type QueryWords } from "../list-query.js";
|
|
4
|
+
export declare const SENDS_QUERY: QueryRegistry;
|
|
5
|
+
export declare const sendFilter: (words: QueryWords) => SendFilter | undefined;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { queryFieldsOf } from "@wirecat/cli-base/query";
|
|
2
|
+
import { sendAttempt } from "../../entities/index.js";
|
|
3
|
+
import { queryTerms } from "../list-query.js";
|
|
4
|
+
export const SENDS_QUERY = {
|
|
5
|
+
entity: "send_attempt",
|
|
6
|
+
backend: "native",
|
|
7
|
+
fields: queryFieldsOf(sendAttempt, {
|
|
8
|
+
native: ["outcome", "chat_id", "operation_id", "error_code", "key"],
|
|
9
|
+
examples: {
|
|
10
|
+
outcome: "outcome:failed",
|
|
11
|
+
chat_id: "chat_id:7",
|
|
12
|
+
operation_id: "operation_id:op-1",
|
|
13
|
+
error_code: "error_code:timeout",
|
|
14
|
+
key: "key:messages.send",
|
|
15
|
+
},
|
|
16
|
+
}).map((field) => field.native
|
|
17
|
+
? { ...field, operators: field.operators.filter((operator) => operator === "term" || operator === "phrase") }
|
|
18
|
+
: field),
|
|
19
|
+
};
|
|
20
|
+
export const sendFilter = (words) => {
|
|
21
|
+
const terms = queryTerms(words, SENDS_QUERY, "sends list");
|
|
22
|
+
if (terms.size === 0)
|
|
23
|
+
return undefined;
|
|
24
|
+
const outcome = sendAttempt.entries.outcome.options.find((value) => value === terms.get("outcome"));
|
|
25
|
+
const chatId = terms.get("chat_id"), operationId = terms.get("operation_id"), errorCode = terms.get("error_code"), key = terms.get("key");
|
|
26
|
+
return {
|
|
27
|
+
...(outcome === undefined ? {} : { outcome }),
|
|
28
|
+
...(chatId === undefined ? {} : { chatId }),
|
|
29
|
+
...(operationId === undefined ? {} : { operationId }),
|
|
30
|
+
...(errorCode === undefined ? {} : { errorCode }),
|
|
31
|
+
...(key === undefined ? {} : { key }),
|
|
32
|
+
};
|
|
33
|
+
};
|
|
@@ -31,6 +31,7 @@ export { lockPath, readLock, servingProfiles } from "../../background/lock.js";
|
|
|
31
31
|
*/
|
|
32
32
|
export const serveCommand = (messenger) => {
|
|
33
33
|
const command = annotate(new Command("serve"), {
|
|
34
|
+
output: { entity: "serve_summary" },
|
|
34
35
|
effects: REMOTE_WRITE,
|
|
35
36
|
lifetime: "persistent",
|
|
36
37
|
examples: [`${messenger.app.command} personal serve`],
|
|
@@ -185,7 +185,6 @@ export const serverCommand = (messenger, options = {}) => {
|
|
|
185
185
|
const withIdle = (sub) => options.idle ? sub.option("--idle <duration>", "stop after this long with nobody using it — 15m, 1h") : sub;
|
|
186
186
|
withIdle(destroysNothing(annotate(command.command("start"), {
|
|
187
187
|
effects: LOCAL_WRITE,
|
|
188
|
-
mcp: false,
|
|
189
188
|
examples: [`${app.command} personal server start`],
|
|
190
189
|
}))
|
|
191
190
|
.summary("start this profile's background server")
|
|
@@ -194,7 +193,6 @@ export const serverCommand = (messenger, options = {}) => {
|
|
|
194
193
|
});
|
|
195
194
|
destroysNothing(annotate(command.command("stop"), {
|
|
196
195
|
effects: LOCAL_WRITE,
|
|
197
|
-
mcp: false,
|
|
198
196
|
examples: [`${app.command} personal server stop`],
|
|
199
197
|
}))
|
|
200
198
|
.description("stop this profile's serve — through the unit if it runs under one")
|
|
@@ -202,7 +200,6 @@ export const serverCommand = (messenger, options = {}) => {
|
|
|
202
200
|
await stop(prepare(this));
|
|
203
201
|
});
|
|
204
202
|
withIdle(destroysNothing(annotate(command.command("restart"), {
|
|
205
|
-
mcp: false,
|
|
206
203
|
effects: LOCAL_WRITE,
|
|
207
204
|
examples: [`${app.command} personal server restart`],
|
|
208
205
|
})).description("stop it and start it again")).action(async function () {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import { type QueryWords } from "../list-query.js";
|
|
3
|
+
export type StatisticsQuery = "contacts-top" | "messages-top" | "discussion" | "unanswered" | "responses";
|
|
4
|
+
export declare const withStatisticsQuery: (command: Command, report: StatisticsQuery) => Command;
|
|
5
|
+
/** Report predicates become the existing source/aggregate parameters; the message query keeps its syntax. */
|
|
6
|
+
export interface StatisticsFilters {
|
|
7
|
+
text?: string;
|
|
8
|
+
messageKind?: string;
|
|
9
|
+
minMessages?: number;
|
|
10
|
+
minViews?: number;
|
|
11
|
+
maxReplies?: number;
|
|
12
|
+
olderThan?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const statisticsQuery: (words: QueryWords, report: StatisticsQuery) => StatisticsFilters;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { annotate } from "@wirecat/cli-base/commands";
|
|
2
|
+
import { defineQuery, fieldsHelp, parseLucene, queryError, validateQuery, walkQuery, } from "@wirecat/cli-base/query";
|
|
3
|
+
import { QUERY_FIELDS } from "../../search/lucene/registry.js";
|
|
4
|
+
import { MESSAGE_PARSING } from "../../search/lucene/types.js";
|
|
5
|
+
import { LIST_QUERY_GUIDE, typedQuery } from "../list-query.js";
|
|
6
|
+
const FIELDS = {
|
|
7
|
+
"contacts-top": ["message_kind", "min_messages"],
|
|
8
|
+
"messages-top": ["message_kind"],
|
|
9
|
+
discussion: ["min_views", "max_replies"],
|
|
10
|
+
unanswered: ["older_than"],
|
|
11
|
+
responses: [],
|
|
12
|
+
};
|
|
13
|
+
const PATHS = {
|
|
14
|
+
"contacts-top": "stats contacts top",
|
|
15
|
+
"messages-top": "stats messages top",
|
|
16
|
+
discussion: "stats messages discussion",
|
|
17
|
+
unanswered: "stats messages unanswered",
|
|
18
|
+
responses: "stats contacts responses",
|
|
19
|
+
};
|
|
20
|
+
const FIELD_SPECS = [
|
|
21
|
+
{
|
|
22
|
+
name: "message_kind",
|
|
23
|
+
type: "enum",
|
|
24
|
+
values: ["all", "posts", "comments"],
|
|
25
|
+
operators: ["term", "phrase"],
|
|
26
|
+
example: "message_kind:posts",
|
|
27
|
+
placement: "and",
|
|
28
|
+
relation: "proven posts or comments selected before ranking",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "min_messages",
|
|
32
|
+
type: "num",
|
|
33
|
+
operators: ["term"],
|
|
34
|
+
example: "min_messages:5",
|
|
35
|
+
placement: "and",
|
|
36
|
+
relation: "minimum selected messages per ranked author",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "min_views",
|
|
40
|
+
type: "num",
|
|
41
|
+
operators: ["term"],
|
|
42
|
+
example: "min_views:10",
|
|
43
|
+
placement: "and",
|
|
44
|
+
relation: "minimum known cumulative views of a discussion post",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "max_replies",
|
|
48
|
+
type: "num",
|
|
49
|
+
operators: ["term"],
|
|
50
|
+
example: "max_replies:0",
|
|
51
|
+
placement: "and",
|
|
52
|
+
relation: "maximum observed discussion replies",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "older_than",
|
|
56
|
+
type: "kw",
|
|
57
|
+
operators: ["term", "phrase"],
|
|
58
|
+
example: "older_than:24h",
|
|
59
|
+
placement: "and",
|
|
60
|
+
relation: "minimum age of an unanswered question; positive duration",
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
const fieldsFor = (report) => FIELD_SPECS.filter(({ name }) => FIELDS[report].includes(name));
|
|
64
|
+
export const withStatisticsQuery = (command, report) => {
|
|
65
|
+
const extra = fieldsFor(report);
|
|
66
|
+
const query = defineQuery({
|
|
67
|
+
entity: report.endsWith("top") ? "ranking" : "admin_report",
|
|
68
|
+
backend: "sql",
|
|
69
|
+
fields: [
|
|
70
|
+
...QUERY_FIELDS.map((field) => ({
|
|
71
|
+
...field,
|
|
72
|
+
example: field.name === "text" ? "text:invoice" : field.name === "topic" ? "topic:42" : field.example,
|
|
73
|
+
})),
|
|
74
|
+
...extra,
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
annotate(command, { query });
|
|
78
|
+
command.addHelpText("after", `\n${fieldsHelp({ fields: extra }, LIST_QUERY_GUIDE)}\nReport predicates are positive AND terms; the remaining query selects stored messages.\n`);
|
|
79
|
+
return command;
|
|
80
|
+
};
|
|
81
|
+
export const statisticsQuery = (words, report) => {
|
|
82
|
+
const source = typedQuery(words);
|
|
83
|
+
if (!source.trim())
|
|
84
|
+
return {};
|
|
85
|
+
const ast = parseLucene(source, MESSAGE_PARSING);
|
|
86
|
+
const reserved = new Set(FIELD_SPECS.map(({ name }) => name));
|
|
87
|
+
const hasReport = (node) => walkQuery(node).some(({ field }) => reserved.has(field));
|
|
88
|
+
if (!hasReport(ast.root))
|
|
89
|
+
return { text: source };
|
|
90
|
+
const context = { command: PATHS[report], guide: LIST_QUERY_GUIDE };
|
|
91
|
+
const values = new Map();
|
|
92
|
+
const original = (node) => {
|
|
93
|
+
const piece = source.slice(node.span.start, node.span.end);
|
|
94
|
+
if (node.kind === "boolean") {
|
|
95
|
+
const prefix = /(?:^|[\s(])([a-z_][a-z0-9_-]*)\s*:\s*$/i.exec(source.slice(0, node.span.start));
|
|
96
|
+
return prefix ? `${prefix[1]}:${piece}` : piece;
|
|
97
|
+
}
|
|
98
|
+
const named = /^([a-z_][a-z0-9_-]*)\s*[:<>]/i.exec(piece)?.[1];
|
|
99
|
+
return node.implicit || named?.toLowerCase() === node.field.toLowerCase() ? piece : `${node.field}:${piece}`;
|
|
100
|
+
};
|
|
101
|
+
const prune = (node) => {
|
|
102
|
+
if (!hasReport(node))
|
|
103
|
+
return original(node);
|
|
104
|
+
if (node.kind === "boolean") {
|
|
105
|
+
if (node.clauses.some(({ occur, node: child }) => occur === "should" || (occur === "must_not" && hasReport(child))))
|
|
106
|
+
queryError("query.unsupported-placement", node.span, "report predicates must be positive AND terms", context);
|
|
107
|
+
const kept = node.clauses
|
|
108
|
+
.map(({ occur, node: child }) => {
|
|
109
|
+
const part = prune(child);
|
|
110
|
+
return part === undefined ? undefined : occur === "must_not" ? `NOT (${part})` : part;
|
|
111
|
+
})
|
|
112
|
+
.filter((part) => part !== undefined);
|
|
113
|
+
return kept.length ? kept.map((part) => `(${part})`).join(" AND ") : undefined;
|
|
114
|
+
}
|
|
115
|
+
if (!FIELDS[report].includes(node.field))
|
|
116
|
+
queryError("query.unsupported-field", node.span, `${node.field} is not a filter for ${PATHS[report]}`, context);
|
|
117
|
+
validateQuery({ ...ast, root: node }, { entity: "statistics", backend: "sql", fields: fieldsFor(report) }, context);
|
|
118
|
+
if (values.has(node.field))
|
|
119
|
+
queryError("query.unsupported-placement", node.span, `${node.field} only once here`, context);
|
|
120
|
+
if (["min_messages", "min_views", "max_replies"].includes(node.field)) {
|
|
121
|
+
const value = Number(node.value);
|
|
122
|
+
if (!Number.isSafeInteger(value) || value < (node.field === "min_messages" ? 1 : 0))
|
|
123
|
+
queryError("query.invalid-number", node.span, `${node.field} takes a ${node.field === "min_messages" ? "positive" : "nonnegative"} integer`, context);
|
|
124
|
+
}
|
|
125
|
+
if (node.field === "older_than") {
|
|
126
|
+
const units = { s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000, w: 604_800_000 };
|
|
127
|
+
const duration = Number(node.value.slice(0, -1)) * (units[node.value.at(-1) ?? ""] ?? 0);
|
|
128
|
+
if (!/^\d+(?:\.\d+)?[smhdw]$/.test(node.value) || !Number.isSafeInteger(duration) || duration <= 0)
|
|
129
|
+
queryError("query.invalid-duration", node.span, "older_than takes a positive duration such as 4h or 7d", context);
|
|
130
|
+
}
|
|
131
|
+
values.set(node.field, node.value);
|
|
132
|
+
return undefined;
|
|
133
|
+
};
|
|
134
|
+
const text = prune(ast.root);
|
|
135
|
+
return {
|
|
136
|
+
...(text ? { text } : {}),
|
|
137
|
+
...(values.has("message_kind") ? { messageKind: values.get("message_kind")?.toLowerCase() } : {}),
|
|
138
|
+
...(values.has("min_messages") ? { minMessages: Number(values.get("min_messages")) } : {}),
|
|
139
|
+
...(values.has("min_views") ? { minViews: Number(values.get("min_views")) } : {}),
|
|
140
|
+
...(values.has("max_replies") ? { maxReplies: Number(values.get("max_replies")) } : {}),
|
|
141
|
+
...(values.has("older_than") ? { olderThan: values.get("older_than") } : {}),
|
|
142
|
+
};
|
|
143
|
+
};
|
|
@@ -91,15 +91,15 @@ export const statsCommand = (messenger, loadRenderer = chartRenderer) => {
|
|
|
91
91
|
"chats retention": '"Book club"',
|
|
92
92
|
"chats show": '"Book club"',
|
|
93
93
|
"contacts evidence": '101 --selection "$selection" --component messages',
|
|
94
|
-
"contacts responses": '
|
|
95
|
-
"contacts top": '
|
|
94
|
+
"contacts responses": 'chat:"Project room"',
|
|
95
|
+
"contacts top": 'chat:"Book club" --limit 10',
|
|
96
96
|
"messages counters refresh": '--chat "Book club" --dry-run',
|
|
97
97
|
"messages counters show": '--chat "Book club" --limit 10',
|
|
98
|
-
"messages discussion": '
|
|
98
|
+
"messages discussion": 'chat:"Book club"',
|
|
99
99
|
"messages evidence": '"$message" --selection "$selection" --component reactions',
|
|
100
100
|
"messages show": '--chat "Book club" --by day',
|
|
101
|
-
"messages top": '
|
|
102
|
-
"messages unanswered": '
|
|
101
|
+
"messages top": 'chat:"Book club" --limit 10',
|
|
102
|
+
"messages unanswered": 'chat:"Project room"',
|
|
103
103
|
"tasks show": '--chat "Project room"',
|
|
104
104
|
};
|
|
105
105
|
for (const [path, args] of Object.entries(examples)) {
|
|
@@ -92,7 +92,7 @@ const refuseWhileServing = (command, messenger) => {
|
|
|
92
92
|
throw new CliError("validation_error", `${messenger.app.command} serve is running for ${serving.join(", ")} — ${serving.map((profile) => `\`${messenger.app.command} ${profile} server stop\``).join(", ")} first`);
|
|
93
93
|
}
|
|
94
94
|
};
|
|
95
|
-
const repairCommand = (messenger) => annotate(new Command("repair"), {
|
|
95
|
+
const repairCommand = (messenger) => annotate(new Command("repair"), { output: { entity: "store_repair" }, effects: LOCAL_WRITE })
|
|
96
96
|
.summary("repair table shapes while keeping the old tables")
|
|
97
97
|
.description("bring every table to this build's shape, deleting nothing: a table of the wrong shape is kept as a copy beside a new one")
|
|
98
98
|
.option("--dry-run", "say what it would do, and change nothing")
|
|
@@ -142,7 +142,7 @@ const notesOf = (report, command) => {
|
|
|
142
142
|
}
|
|
143
143
|
return notes;
|
|
144
144
|
};
|
|
145
|
-
const copiesCommand = () => new Command("copies").description("the tables `store repair` kept as copies").addCommand(annotate(new Command("delete"), {
|
|
145
|
+
const copiesCommand = () => new Command("copies").description("the tables `store repair` kept as copies").addCommand(annotate(new Command("delete"), { output: { entity: "store_copy_removal" }, effects: LOCAL_WRITE })
|
|
146
146
|
.description("delete one copy `store repair` kept, named exactly; refuses any other table")
|
|
147
147
|
.argument("<name>", "the copy's name, as `store repair` printed it")
|
|
148
148
|
.action(async function (name) {
|
|
@@ -152,7 +152,7 @@ const copiesCommand = () => new Command("copies").description("the tables `store
|
|
|
152
152
|
const deleted = await reading(path, (database) => deleteCopy(database, name));
|
|
153
153
|
result(this, { path, deleted });
|
|
154
154
|
}));
|
|
155
|
-
const infoCommand = () => annotate(new Command("info"), { effects: LOCAL_READ })
|
|
155
|
+
const infoCommand = () => annotate(new Command("info"), { output: { entity: "store_info" }, effects: LOCAL_READ })
|
|
156
156
|
.summary("the local archive's format, size and contents")
|
|
157
157
|
.description("the store file: where it is, its size, its schema and how many rows it holds; changes nothing")
|
|
158
158
|
.action(async function () {
|
|
@@ -182,7 +182,7 @@ const infoCommand = () => annotate(new Command("info"), { effects: LOCAL_READ })
|
|
|
182
182
|
* fetched" (NEED-399 A). Times are the chat's newest message by the messenger's list against the
|
|
183
183
|
* newest message held, not `sync_ranges`: those are in message ids, which a chat list does not give.
|
|
184
184
|
*/
|
|
185
|
-
const checkCommand = (messenger) => annotate(new Command("check"), { effects: LOCAL_READ })
|
|
185
|
+
const checkCommand = (messenger) => annotate(new Command("check"), { output: { entity: "store_check" }, effects: LOCAL_READ })
|
|
186
186
|
.summary("check the local archive for problems")
|
|
187
187
|
.description("whether the store is healthy — integrity, search indexes, disk, and which chats are behind")
|
|
188
188
|
.action(async function () {
|
|
@@ -362,7 +362,7 @@ const chatsBehind = (database) => database
|
|
|
362
362
|
held: row.held === null ? null : isoOf(row.held),
|
|
363
363
|
refreshed: isoOf(row.refreshed),
|
|
364
364
|
}));
|
|
365
|
-
const migrateCommand = (messenger) => annotate(new Command("migrate"), {
|
|
365
|
+
const migrateCommand = (messenger) => annotate(new Command("migrate"), { output: { entity: "store_migration" }, effects: LOCAL_WRITE })
|
|
366
366
|
.summary("migrate an older local archive")
|
|
367
367
|
.description("bring the store up to this build's schema, then normalize, index and stem the messages and notes stored before it")
|
|
368
368
|
.action(async function () {
|
|
@@ -431,7 +431,7 @@ const buildNoteIndex = (database, note, { reset = false } = {}) => {
|
|
|
431
431
|
note(`indexing ${pending} notes`);
|
|
432
432
|
return { notesIndexed: drainNoteIndex(database, stemmerCache()) };
|
|
433
433
|
};
|
|
434
|
-
const reindexCommand = (messenger) => destroysNothing(annotate(new Command("reindex"), { effects: LOCAL_WRITE }))
|
|
434
|
+
const reindexCommand = (messenger) => destroysNothing(annotate(new Command("reindex"), { output: { entity: "store_reindex" }, effects: LOCAL_WRITE }))
|
|
435
435
|
.summary("rebuild the local archive's search indexes")
|
|
436
436
|
.description("rebuild the word index, its typo vocabulary, the stems, the files' word index, the notes' indexes and who took part in what, from what is stored; loses nothing")
|
|
437
437
|
.action(async function () {
|
|
@@ -478,7 +478,7 @@ const vacuumInto = async (path, target) => {
|
|
|
478
478
|
}
|
|
479
479
|
};
|
|
480
480
|
const stampNow = () => new Date().toISOString().replace(/[:.]/g, "-");
|
|
481
|
-
const backupCommand = () => destroysNothing(annotate(new Command("backup"), {
|
|
481
|
+
const backupCommand = () => destroysNothing(annotate(new Command("backup"), { output: { entity: "store_backup" }, effects: LOCAL_WRITE }))
|
|
482
482
|
.description("copy the store into a new file, while it is in use; never overwrites a file")
|
|
483
483
|
.argument("<file>", "the new file")
|
|
484
484
|
.option(...ENCRYPT_OPTION)
|
|
@@ -522,7 +522,7 @@ const backupCommand = () => destroysNothing(annotate(new Command("backup"), { ef
|
|
|
522
522
|
}
|
|
523
523
|
});
|
|
524
524
|
/** The only way back into a sealed file: the format is this tool's own. */
|
|
525
|
-
const decryptCommand = () => annotate(new Command("decrypt"), {
|
|
525
|
+
const decryptCommand = () => annotate(new Command("decrypt"), { output: { entity: "store_decrypt" }, effects: LOCAL_WRITE })
|
|
526
526
|
.description("open a file written with --encrypt into a new file; asks for its password")
|
|
527
527
|
.argument("<file>", "a file `store backup --encrypt` or `store export --encrypt` wrote")
|
|
528
528
|
.requiredOption("--output <file>", "the new file, readable only by you")
|
|
@@ -544,7 +544,7 @@ const decryptCommand = () => annotate(new Command("decrypt"), { effects: LOCAL_W
|
|
|
544
544
|
* process has the file open — it would go on writing to the file set aside — or while this CLI's
|
|
545
545
|
* `serve` runs: that opens the store on its first write, so until then it holds nothing to see.
|
|
546
546
|
*/
|
|
547
|
-
const restoreCommand = (messenger) => annotate(new Command("restore"), {
|
|
547
|
+
const restoreCommand = (messenger) => annotate(new Command("restore"), { output: { entity: "store_restore" }, effects: LOCAL_WRITE })
|
|
548
548
|
.summary("restore a backup while keeping the current store")
|
|
549
549
|
.description("put a backup in place of the store; the store it replaces is kept beside it, never deleted")
|
|
550
550
|
.argument("<file>", "a file `store backup` wrote; one written with --encrypt asks for its password")
|
|
@@ -661,7 +661,7 @@ const quiesce = async (path, messenger, then, warn, waitMs = STORE_LOCK_WAIT_MS)
|
|
|
661
661
|
* For a store this build cannot migrate: a backup beside it, then an empty store at this build's
|
|
662
662
|
* schema. The backup is made before anything is deleted, and its path is said before the delete.
|
|
663
663
|
*/
|
|
664
|
-
const resetCommand = (messenger) => annotate(new Command("reset"), {
|
|
664
|
+
const resetCommand = (messenger) => annotate(new Command("reset"), { output: { entity: "store_reset" }, effects: LOCAL_WRITE })
|
|
665
665
|
.summary("back up the local archive and replace it with an empty one")
|
|
666
666
|
.description("back the store up beside itself, then delete it and start an empty one at this build's schema; asks first, or --yes")
|
|
667
667
|
.option("--no-backup", "delete the store without backing it up first")
|
|
@@ -417,7 +417,10 @@ export const stored = (messenger, { account, store, warn, events, pending, annou
|
|
|
417
417
|
const sent = await messenger.send(chatId, text, options);
|
|
418
418
|
// A scheduled message is not in the chat yet, and it will arrive under another id.
|
|
419
419
|
if (options.at === undefined) {
|
|
420
|
-
await save("messages.send", (opened) =>
|
|
420
|
+
await save("messages.send", async (opened) => {
|
|
421
|
+
for (const message of sent.messages ?? [sent.message])
|
|
422
|
+
await saveSent(opened, account, chatId, message, { via: "send" });
|
|
423
|
+
});
|
|
421
424
|
}
|
|
422
425
|
return sent;
|
|
423
426
|
},
|
|
@@ -32,7 +32,11 @@ export const tagsCommand = (messenger) => {
|
|
|
32
32
|
const tags = new Command("tags")
|
|
33
33
|
.summary("tags on stored messages and other records")
|
|
34
34
|
.description("your own labels on chats, people, messages and tasks, kept in the local store and never sent; tag: in a search finds them");
|
|
35
|
-
changesStore(annotate(tags.command("auto"), {
|
|
35
|
+
changesStore(annotate(tags.command("auto"), {
|
|
36
|
+
output: { entity: "automatic_tags" },
|
|
37
|
+
effects: FETCH,
|
|
38
|
+
examples: [`${tool} tags auto --chat "Book club" --dry-run`],
|
|
39
|
+
}))
|
|
36
40
|
.description("derive local group/channel tags from cached metadata using keyword rules")
|
|
37
41
|
.option("--chat <chat>", "a stored group/channel; repeat to select several", (value, previous) => [...previous, value], [])
|
|
38
42
|
.option("--limit <n>", "process at most 1–500 chats", positiveCount("--limit"), 50)
|
|
@@ -47,6 +51,7 @@ export const tagsCommand = (messenger) => {
|
|
|
47
51
|
renderAnswer(this, context, itemOf(snakeKeys(done)));
|
|
48
52
|
});
|
|
49
53
|
targetOptions(annotate(tags.command("add"), {
|
|
54
|
+
output: { entity: "tag_change" },
|
|
50
55
|
effects: LOCAL_WRITE,
|
|
51
56
|
examples: [`${tool} tags add reading --chat "Book club"`],
|
|
52
57
|
}).description("put tags on one chat, person, message or task"), messenger, "tag").action(async function (given) {
|
|
@@ -59,6 +64,7 @@ export const tagsCommand = (messenger) => {
|
|
|
59
64
|
context.renderer.note(`already there: ${done.unchanged.join(", ")}`);
|
|
60
65
|
});
|
|
61
66
|
targetOptions(annotate(tags.command("remove"), {
|
|
67
|
+
output: { entity: "tag_change" },
|
|
62
68
|
effects: LOCAL_WRITE,
|
|
63
69
|
examples: [`${tool} tags remove reading --chat "Book club"`],
|
|
64
70
|
}).description("take tags off one chat, person, message or task"), messenger, "untag")
|