@wirecat/cli-core 0.395.0 → 0.397.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 +63 -1
- package/README.md +1 -0
- package/dist/background/daily-archive-timer.d.ts +40 -0
- package/dist/background/daily-archive-timer.js +181 -0
- package/dist/background/index.d.ts +2 -0
- package/dist/background/index.js +1 -0
- package/dist/cli/commands-command.js +6 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/input-fields.d.ts +3 -0
- package/dist/cli/input-fields.js +27 -0
- package/dist/cli/messenger/account-command.js +1 -0
- package/dist/cli/messenger/account-sessions-command.js +3 -1
- package/dist/cli/messenger/admin-commands.js +1 -0
- package/dist/cli/messenger/admin-contacts-command.js +9 -3
- package/dist/cli/messenger/archive-commands.js +3 -1
- package/dist/cli/messenger/attachments-command.js +32 -10
- package/dist/cli/messenger/backfill-command.js +4 -0
- package/dist/cli/messenger/chats-command.js +1 -0
- package/dist/cli/messenger/chats-members-command.js +4 -0
- package/dist/cli/messenger/chats-stats-command.js +1 -0
- package/dist/cli/messenger/chats-tracking-command.js +4 -1
- package/dist/cli/messenger/complete-command.js +1 -1
- package/dist/cli/messenger/contacts-command.js +9 -3
- package/dist/cli/messenger/conversation-answers.d.ts +5 -0
- package/dist/cli/messenger/conversations-command.js +18 -1
- package/dist/cli/messenger/counters-command.js +1 -0
- package/dist/cli/messenger/download-command.js +1 -0
- package/dist/cli/messenger/flood-command.js +1 -0
- package/dist/cli/messenger/guard-commands.js +5 -4
- package/dist/cli/messenger/inbox.js +1 -0
- package/dist/cli/messenger/mcp-command.js +6 -2
- package/dist/cli/messenger/media-archive-command.d.ts +3 -0
- package/dist/cli/messenger/media-archive-command.js +72 -0
- package/dist/cli/messenger/media-archive-move-command.d.ts +3 -0
- package/dist/cli/messenger/media-archive-move-command.js +40 -0
- package/dist/cli/messenger/media-archive-timer-command.d.ts +3 -0
- package/dist/cli/messenger/media-archive-timer-command.js +70 -0
- package/dist/cli/messenger/message-answers.d.ts +5 -0
- package/dist/cli/messenger/message-writes.d.ts +5 -0
- package/dist/cli/messenger/messages-comments-command.js +1 -0
- package/dist/cli/messenger/messages-delete-command.js +1 -1
- package/dist/cli/messenger/messages-search-command.js +1 -0
- package/dist/cli/messenger/messages-stats-command.js +1 -0
- package/dist/cli/messenger/models-command.js +3 -0
- package/dist/cli/messenger/private-people-command.js +12 -10
- package/dist/cli/messenger/rankings-command.js +2 -0
- package/dist/cli/messenger/reactions-command.js +8 -2
- package/dist/cli/messenger/retention-command.js +1 -0
- package/dist/cli/messenger/review.js +1 -0
- package/dist/cli/messenger/search-answers.js +2 -0
- package/dist/cli/messenger/search-command.js +5 -1
- package/dist/cli/messenger/searches-command.d.ts +2 -0
- package/dist/cli/messenger/searches-command.js +26 -8
- package/dist/cli/messenger/server-command.js +7 -2
- package/dist/cli/messenger/store-maintenance-command.js +7 -0
- package/dist/cli/messenger/tags-command.js +6 -2
- package/dist/cli/messenger/tasks-command.js +10 -7
- package/dist/cli/messenger/transcribe-command.js +1 -0
- package/dist/cli/messenger/watch-command.js +1 -0
- package/dist/cli/program.js +5 -2
- package/dist/entities/contact-read.d.ts +119 -0
- package/dist/entities/contact-read.js +50 -0
- package/dist/entities/evidence.d.ts +40 -0
- package/dist/entities/index.d.ts +191 -2
- package/dist/entities/index.js +7 -1
- package/dist/entities/message.d.ts +9 -0
- package/dist/entities/message.js +6 -0
- package/dist/errors/reasons.d.ts +1 -0
- package/dist/errors/reasons.js +15 -0
- package/dist/meetings/mcp/index.d.ts +15 -15
- package/dist/parity/catalogue.js +1 -1
- package/dist/parity/manifest.d.ts +3 -1
- package/dist/parity/manifest.js +2 -0
- package/dist/parity/wording.js +2 -0
- package/dist/search/lucene/registry.js +2 -2
- package/dist/sends/permissions.js +7 -0
- package/dist/services/index.d.ts +5 -0
- package/dist/services/index.js +2 -0
- package/dist/services/media-archive-files.d.ts +10 -0
- package/dist/services/media-archive-files.js +110 -0
- package/dist/services/media-archive-locks.d.ts +6 -0
- package/dist/services/media-archive-locks.js +71 -0
- package/dist/services/media-archive-manifest.d.ts +29 -0
- package/dist/services/media-archive-manifest.js +116 -0
- package/dist/services/media-archive-move.d.ts +21 -0
- package/dist/services/media-archive-move.js +163 -0
- package/dist/services/media-archive.d.ts +34 -0
- package/dist/services/media-archive.js +165 -0
- package/dist/services/messages-search.js +13 -2
- package/dist/services/messages.d.ts +5 -0
- package/dist/store/search-index.d.ts +4 -1
- package/dist/store/search-index.js +10 -1
- package/dist/store/sqlite/hit-places.d.ts +12 -0
- package/dist/store/sqlite/hit-places.js +76 -0
- package/dist/store/sqlite/lucene-fields.d.ts +8 -0
- package/dist/store/sqlite/lucene-fields.js +6 -4
- package/dist/store/sqlite/lucene.js +25 -70
- package/dist/store/sqlite/manifest.js +2 -0
- package/dist/store/sqlite/migrations.generated.js +19 -0
- package/dist/store/sqlite/note-index.d.ts +11 -1
- package/dist/store/sqlite/note-index.js +18 -6
- package/dist/store/sqlite/schema.d.ts +39 -0
- package/dist/store/sqlite/schema.js +4 -0
- package/dist/store/sqlite/transcript-query.d.ts +40 -0
- package/dist/store/sqlite/transcript-query.js +108 -0
- package/dist/store/sqlite/words.d.ts +6 -0
- package/package.json +3 -3
- package/parity.json +50 -1
|
@@ -7,7 +7,9 @@ import { lines, wholeList } from "./chat-answers.js";
|
|
|
7
7
|
import { messengerContext, refuseLocalWrite } from "./context.js";
|
|
8
8
|
/** The chats whose member lists `serve` fetches daily; kept in the local store, never sent. */
|
|
9
9
|
export const trackingCommand = (messenger) => {
|
|
10
|
-
const tracking = new Command("tracking")
|
|
10
|
+
const tracking = new Command("tracking")
|
|
11
|
+
.summary("chats whose member lists are fetched daily")
|
|
12
|
+
.description("the chats whose member lists serve fetches daily into the local store — chats members fetch --track adds one");
|
|
11
13
|
annotate(tracking.command("list"), { effects: LOCAL_READ })
|
|
12
14
|
.description("every tracked chat: since when, and its last member count")
|
|
13
15
|
.action(async function () {
|
|
@@ -22,6 +24,7 @@ export const trackingCommand = (messenger) => {
|
|
|
22
24
|
].join(" ")));
|
|
23
25
|
});
|
|
24
26
|
annotate(tracking.command("show"), { effects: LOCAL_READ })
|
|
27
|
+
.summary("one chat's member tracking and daily counts")
|
|
25
28
|
.description(`one chat: whether it is tracked, and its member count per day for the last ${TRACKED_DAYS} days`)
|
|
26
29
|
.argument("<chat>", messenger.chatArgument)
|
|
27
30
|
.action(async function (chat) {
|
|
@@ -21,7 +21,7 @@ const SHELLS = ["zsh", "bash", "fish", "powershell"];
|
|
|
21
21
|
*/
|
|
22
22
|
export const completeCommand = (messenger, config, options = {}) => annotate(new Command("complete"), { effects: LOCAL_READ, mcp: false })
|
|
23
23
|
.description(`shell completion: \`${messenger.app.command} complete zsh\` prints the script to source`)
|
|
24
|
-
.argument("[words...]")
|
|
24
|
+
.argument("[words...]", "zsh for the shell script, or command words to complete")
|
|
25
25
|
.allowUnknownOption()
|
|
26
26
|
.helpOption(false)
|
|
27
27
|
.action(async function (words) {
|
|
@@ -68,7 +68,8 @@ export const contactsCommand = (messenger) => {
|
|
|
68
68
|
}));
|
|
69
69
|
renderAnswer(this, context, itemOf(contactAnswer(account, card)));
|
|
70
70
|
}));
|
|
71
|
-
annotate(contacts.command("profile"), { effects: REMOTE_READ })
|
|
71
|
+
annotate(contacts.command("profile"), { effects: REMOTE_READ, output: { entity: "contact_profile" } })
|
|
72
|
+
.summary("a person's profile from the messenger")
|
|
72
73
|
.description("everything the messenger says about one person — handles, flags, last seen, when they registered — and " +
|
|
73
74
|
"how many of their messages the store holds in each chat you share, the first and the last, and the " +
|
|
74
75
|
"earlier names and usernames the store saw them with")
|
|
@@ -86,6 +87,7 @@ export const contactsCommand = (messenger) => {
|
|
|
86
87
|
}));
|
|
87
88
|
});
|
|
88
89
|
const contextCommand = annotate(contacts.command("context"), { effects: REMOTE_READ })
|
|
90
|
+
.summary("what the local archive knows about a person")
|
|
89
91
|
.description("what the store holds about one person, in every messenger linked to them: shared chats, the last " +
|
|
90
92
|
"messages each way, their recent messages, where others mentioned them — never connects")
|
|
91
93
|
.argument("<person>", "their id, @username, or part of their name")
|
|
@@ -167,6 +169,7 @@ export const contactsCommand = (messenger) => {
|
|
|
167
169
|
}));
|
|
168
170
|
});
|
|
169
171
|
annotate(contacts.command("timeline"), { effects: REMOTE_READ })
|
|
172
|
+
.summary("one person's activity across their linked identities")
|
|
170
173
|
.description("everything one person took part in, in every messenger linked to them — messages they wrote or were " +
|
|
171
174
|
"mentioned in, chats, mail, meetings, tasks — newest first, from the store; never connects")
|
|
172
175
|
.argument("<person>", "their id, @username, or part of their name")
|
|
@@ -208,6 +211,7 @@ export const contactsCommand = (messenger) => {
|
|
|
208
211
|
}));
|
|
209
212
|
});
|
|
210
213
|
annotate(contacts.command("check"), { effects: REMOTE_READ })
|
|
214
|
+
.summary("check a person's profile and messages for signs of abuse")
|
|
211
215
|
.description(registriesCover(messenger.provider)
|
|
212
216
|
? "whether one person looks like a bot, a fake or a spammer: their profile, what they wrote in the store, " +
|
|
213
217
|
`and the public ban lists (${Object.values(REGISTRIES)
|
|
@@ -235,6 +239,7 @@ export const contactsCommand = (messenger) => {
|
|
|
235
239
|
renderAnswer(this, context, itemOf(snakeKeys(answer)));
|
|
236
240
|
});
|
|
237
241
|
annotate(contacts.command("link"), { effects: LOCAL_WRITE })
|
|
242
|
+
.summary("link identities that belong to one person")
|
|
238
243
|
.description("record that two people in the store are one person — the same name is never enough")
|
|
239
244
|
.argument("<person>", "their id, @username, or part of their name")
|
|
240
245
|
.argument("<other>", "the same in another messenger of the store, as <messenger>:<person> — max:Ana; me: it is you, as zoom:<id> me")
|
|
@@ -255,7 +260,8 @@ export const contactsCommand = (messenger) => {
|
|
|
255
260
|
* ⚠ **The number is asked for or piped, never an argument**: argv is read by `ps` and kept by shell
|
|
256
261
|
* history, and a phone number is personal data.
|
|
257
262
|
*/
|
|
258
|
-
annotate(contacts.command("lookup"), { effects: REMOTE_READ })
|
|
263
|
+
annotate(contacts.command("lookup"), { effects: REMOTE_READ, output: { entity: "contact_lookup" } })
|
|
264
|
+
.summary("find an account by a phone number")
|
|
259
265
|
.description("who has this phone number — asks for it, or reads it from stdin; never an argument")
|
|
260
266
|
// Commander's own refusal would repeat the number on stderr.
|
|
261
267
|
.allowExcessArguments()
|
|
@@ -267,7 +273,7 @@ export const contactsCommand = (messenger) => {
|
|
|
267
273
|
const phone = phoneOf(await readSecret("phone number: ", { ...secretInputOf(this), echo: true }));
|
|
268
274
|
renderAnswer(this, context, itemOf(snakeKeys(await context.withServices((services) => services.people.lookup(phone)))));
|
|
269
275
|
});
|
|
270
|
-
annotate(contacts.command("sync"), { effects: FETCH })
|
|
276
|
+
annotate(contacts.command("sync"), { effects: FETCH, output: { entity: "contact_sync" } })
|
|
271
277
|
.description("take the whole contact list from the messenger into the local store")
|
|
272
278
|
.action(async function () {
|
|
273
279
|
const context = messengerContext(this, messenger);
|
|
@@ -54,6 +54,11 @@ export declare const conversationShown: (account: AccountKey, { summary, message
|
|
|
54
54
|
excerpt?: string | undefined;
|
|
55
55
|
matched_in?: "context" | "file" | "filename" | "picture" | "text" | "transcript" | undefined;
|
|
56
56
|
score?: number | undefined;
|
|
57
|
+
match?: "beginnings" | "corrected" | "words" | undefined;
|
|
58
|
+
corrections?: {
|
|
59
|
+
from: string;
|
|
60
|
+
to: string[];
|
|
61
|
+
}[] | undefined;
|
|
57
62
|
found_in?: "archive" | "both" | "server" | undefined;
|
|
58
63
|
discovery?: {
|
|
59
64
|
coverage: number;
|
|
@@ -30,8 +30,11 @@ import { readAll } from "./stdin.js";
|
|
|
30
30
|
* mentions, one sender's messages in a row. From the store alone; nothing is built until asked (phase 3).
|
|
31
31
|
*/
|
|
32
32
|
export const conversationsCommand = (messenger) => {
|
|
33
|
-
const conversations = new Command("conversations")
|
|
33
|
+
const conversations = new Command("conversations")
|
|
34
|
+
.summary("group stored messages into conversations")
|
|
35
|
+
.description("the conversations inside a chat, found in the stored messages by replies, mentions and who wrote next");
|
|
34
36
|
boundOptions(reachesOtherService(annotate(conversations.command("build"), { effects: REMOTE_WRITE }))
|
|
37
|
+
.summary("build conversation links from stored messages")
|
|
35
38
|
.description("find a chat's conversations in what the store holds, replacing the last build; without --chat, every " +
|
|
36
39
|
"chat that changed since its build and every group never built; never asks the messenger")
|
|
37
40
|
.option("--chat <chat>", messenger.chatArgument)
|
|
@@ -111,6 +114,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
111
114
|
renderAnswer(this, context, itemOf(snakeKeys(built)), () => `${built.messages} messages → ${built.conversations} conversations, ${built.links} links (rules v${built.rulesVersion})\n`);
|
|
112
115
|
});
|
|
113
116
|
withQuery(annotate(conversations.command("list"), { effects: LOCAL_READ })
|
|
117
|
+
.summary("stored conversations in one chat")
|
|
114
118
|
.description("a chat's conversations, the newest first: when, how many messages, how many people")
|
|
115
119
|
.argument("<chat>", messenger.chatArgument), CONVERSATION_QUERY)
|
|
116
120
|
.option("--limit <n>", "how many", positiveCount("--limit"))
|
|
@@ -123,6 +127,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
123
127
|
context.renderer.note("no conversations match");
|
|
124
128
|
});
|
|
125
129
|
annotate(conversations.command("show"), { effects: LOCAL_READ })
|
|
130
|
+
.summary("one conversation and its messages")
|
|
126
131
|
.description("one conversation's messages, oldest first — by its id, or the one a message is in")
|
|
127
132
|
.argument("<conversation>", `a conversation id from \`conversations list\`; or ${messenger.chatArgument}, with a message`)
|
|
128
133
|
.argument("[message]", "a message id in that chat: show the conversation it is in")
|
|
@@ -151,6 +156,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
151
156
|
renderAnswer(this, context, conversationShown(account, { summary, messages }));
|
|
152
157
|
});
|
|
153
158
|
withModelOptions(annotate(conversations.command("related"), { effects: LOCAL_READ })
|
|
159
|
+
.summary("conversations nearest in meaning to a message's conversation")
|
|
154
160
|
.description("the conversations nearest in meaning to the one a message is in, in every built chat, best first — " +
|
|
155
161
|
"from the vectors `conversations embed` stored; runs no model")
|
|
156
162
|
.argument("<chat>", messenger.chatArgument)
|
|
@@ -166,6 +172,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
166
172
|
context.renderer.note(`no other conversation has vectors of ${found.model}`);
|
|
167
173
|
});
|
|
168
174
|
withModelOptions(annotate(conversations.command("status"), { effects: LOCAL_READ })
|
|
175
|
+
.summary("which chats are ready for conversation search")
|
|
169
176
|
.description("how fresh each built chat's conversations and vectors are: messages the build has not seen, chunks " +
|
|
170
177
|
"with a current, stale or missing vector")
|
|
171
178
|
.option("--chat <chat>", `only this chat: ${messenger.chatArgument}`)).action(async function () {
|
|
@@ -179,9 +186,11 @@ export const conversationsCommand = (messenger) => {
|
|
|
179
186
|
});
|
|
180
187
|
const batches = conversations
|
|
181
188
|
.command("batches")
|
|
189
|
+
.summary("batches waiting for an agent's conversation links")
|
|
182
190
|
.description("windows of a chat for your own AI agent to link: which earlier message each one answers");
|
|
183
191
|
const sizeOption = (command) => command.option("--size <n>", `messages to answer per batch, ${BATCH_SIZE.min}–${BATCH_SIZE.max}; ${BATCH_SIZE.default} by default`, positiveCount("--size"));
|
|
184
192
|
sizeOption(annotate(batches.command("status"), { effects: LOCAL_READ })
|
|
193
|
+
.summary("progress of conversation-link batches")
|
|
185
194
|
.description("how many messages still wait for an answer, in how many batches, and how much text")
|
|
186
195
|
.requiredOption("--chat <chat>", messenger.chatArgument)).action(async function () {
|
|
187
196
|
const { chat, size = BATCH_SIZE.default } = this.opts();
|
|
@@ -191,6 +200,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
191
200
|
`${status.characters} characters (about ${status.tokensEstimate} tokens)\n`);
|
|
192
201
|
});
|
|
193
202
|
sizeOption(annotate(batches.command("next"), { effects: LOCAL_READ })
|
|
203
|
+
.summary("get the next batch of messages to link")
|
|
194
204
|
.description("the next window to answer, with the messages before it; message text goes to stdout only")
|
|
195
205
|
.requiredOption("--chat <chat>", messenger.chatArgument)).action(async function () {
|
|
196
206
|
const { chat, size = BATCH_SIZE.default } = this.opts();
|
|
@@ -221,6 +231,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
221
231
|
description: '{ "model", "skill"?, "answers": [{ "message", "parent", "confidence" }] }',
|
|
222
232
|
json: true,
|
|
223
233
|
})
|
|
234
|
+
.summary("save an agent's conversation-link answers")
|
|
224
235
|
.description('store your agent\'s answer to a batch, read as JSON from stdin: { "model", "answers": [{ "message", ' +
|
|
225
236
|
'"parent", "confidence" }] }; all or nothing')
|
|
226
237
|
.requiredOption("--batch <id>", "the batch id `conversations batches next` printed")
|
|
@@ -233,6 +244,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
233
244
|
renderAnswer(this, context, itemOf(stored, { next: [nextStep("conversations.build", { args: { chat: stored.chat } })] }), () => `${stored.stored} answers stored\n`);
|
|
234
245
|
});
|
|
235
246
|
annotate(links.command("clear"), { effects: LOCAL_WRITE })
|
|
247
|
+
.summary("remove an agent's saved conversation-link answers")
|
|
236
248
|
.description("drop your agent's answers for a chat, or only one model's; messages are never touched")
|
|
237
249
|
.requiredOption("--chat <chat>", messenger.chatArgument)
|
|
238
250
|
.option("--model <model>", "only the answers this model gave")
|
|
@@ -244,6 +256,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
244
256
|
renderAnswer(this, context, itemOf(cleared), () => `${cleared.cleared} answers dropped\n`);
|
|
245
257
|
});
|
|
246
258
|
const embed = withModelOptions(reachesOtherService(annotate(new Command("embed"), { effects: REMOTE_WRITE }))
|
|
259
|
+
.summary("prepare conversation vectors for meaning search")
|
|
247
260
|
.description("compute a vector for each chunk of a chat's conversations for search by meaning — on this machine, or " +
|
|
248
261
|
"with --provider through a service and your key; resumes where it stopped; without --chat, every " +
|
|
249
262
|
"built chat with chunks left, on this machine only")
|
|
@@ -290,6 +303,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
290
303
|
}), () => `${done.embedded} chunks embedded with ${done.model}${done.skipped ? `, ${done.skipped} changed since the build` : ""}\n`);
|
|
291
304
|
});
|
|
292
305
|
withModelOptions(annotate(embed.command("status"), { effects: LOCAL_READ })
|
|
306
|
+
.summary("which conversation vectors are ready or stale")
|
|
293
307
|
.description("how many chunks of a chat have a vector of the model, how many are left, and what is left costs")
|
|
294
308
|
.option("--chat <chat>", messenger.chatArgument)).action(async function () {
|
|
295
309
|
const options = this.optsWithGlobals();
|
|
@@ -303,6 +317,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
303
317
|
"\n");
|
|
304
318
|
});
|
|
305
319
|
withModelOptions(destroysNothing(annotate(embed.command("clear"), { effects: LOCAL_WRITE }))
|
|
320
|
+
.summary("remove stored conversation vectors")
|
|
306
321
|
.description("drop a chat's vectors, or only one model's; messages and conversations are never touched")
|
|
307
322
|
.option("--chat <chat>", messenger.chatArgument)).action(async function () {
|
|
308
323
|
const options = this.optsWithGlobals();
|
|
@@ -479,6 +494,7 @@ export const linksCommand = (messenger) => annotate(new Command("links"), {
|
|
|
479
494
|
examples: [`${messenger.app.command} personal messages links "Book club" 101`],
|
|
480
495
|
effects: LOCAL_READ,
|
|
481
496
|
})
|
|
497
|
+
.summary("why a message belongs to its conversation")
|
|
482
498
|
.description("why a message is in its conversation: each link it has, and the chain of answers back to the start")
|
|
483
499
|
.argument("<chat>", messenger.chatArgument)
|
|
484
500
|
.argument("<message>", "the message id")
|
|
@@ -511,6 +527,7 @@ const readinessLine = ({ chat, state, graph, pending, vectors }, model) => graph
|
|
|
511
527
|
`${vectors.missing} missing of ${vectors.chunks} chunks`;
|
|
512
528
|
/** `search conversations`: the conversations nearest to a query, in meaning and in words. */
|
|
513
529
|
export const conversationsSearchCommand = (messenger) => syncOptions(boundOptions(withModelOptions(reachesOtherService(annotate(new Command("conversations"), { effects: REMOTE_READ })))), messenger)
|
|
530
|
+
.summary("find conversations by words and meaning")
|
|
514
531
|
.description("the conversations nearest to a query in meaning and in words, best first, in one chat or every one — " +
|
|
515
532
|
"meaning after `conversations embed`; runs on this machine")
|
|
516
533
|
.argument("<query>", "what to look for, in your own words, in any language the model reads")
|
|
@@ -10,6 +10,7 @@ export const countersCommand = (messenger) => {
|
|
|
10
10
|
const parent = new Command("counters").description("per-counter observations and bounded remote refresh");
|
|
11
11
|
for (const operation of ["show", "refresh"]) {
|
|
12
12
|
const command = new Command(operation)
|
|
13
|
+
.summary("refresh message counters from the messenger")
|
|
13
14
|
.description(operation === "show"
|
|
14
15
|
? "show saved counter values and their observation freshness"
|
|
15
16
|
: "read authoritative counters for bounded messages and update their local observations")
|
|
@@ -21,6 +21,7 @@ export const downloadSubcommand = (messages, messenger) => annotate(messages.com
|
|
|
21
21
|
examples: [`${messenger.app.command} personal messages download "Book club" 101 --output-dir ./downloads`],
|
|
22
22
|
effects: FETCH,
|
|
23
23
|
})
|
|
24
|
+
.summary("download files attached to stored messages")
|
|
24
25
|
.description("save a message's photos, files, videos and voice notes to a folder — or a whole chat's with --all")
|
|
25
26
|
.argument("<chat>", messenger.chatArgument)
|
|
26
27
|
.argument("[message]", "the message id; left out with --all")
|
|
@@ -15,6 +15,7 @@ export const floodCommand = (messenger) => {
|
|
|
15
15
|
return annotate(new Command("flood"), { mcp: false })
|
|
16
16
|
.description(`the waits ${name} asked this profile to keep, and a hold on its writes`)
|
|
17
17
|
.addCommand(grantsAuthority(annotate(new Command("clear"), { effects: LOCAL_WRITE }))
|
|
18
|
+
.summary("clear a profile's stored rate-limit hold")
|
|
18
19
|
.description(`forget them, lift the hold and the profile's pace, once ${name} no longer limits the account; changes nothing there`)
|
|
19
20
|
.action(async function () {
|
|
20
21
|
const context = messengerContext(this, messenger);
|
|
@@ -14,9 +14,10 @@ import { messengerContext } from "./context.js";
|
|
|
14
14
|
* a model that a message talked into writing somewhere, not an agent set on getting around it.
|
|
15
15
|
*/
|
|
16
16
|
export const recipientsCommand = (messenger) => {
|
|
17
|
+
const tool = `${messenger.app.command} personal`;
|
|
17
18
|
const listFor = (profile, env) => new RecipientList(recipientsPathFor(messenger.app, profile, env), messenger.app.command);
|
|
18
19
|
const command = new Command("recipients").description("the chats this profile may send to, when the list is on");
|
|
19
|
-
annotate(command.command("list"), { effects: LOCAL_READ })
|
|
20
|
+
annotate(command.command("list"), { effects: LOCAL_READ, examples: [`${tool} recipients list`] })
|
|
20
21
|
.description("the chats on the list; empty and off until the first add")
|
|
21
22
|
.action(async function () {
|
|
22
23
|
const context = messengerContext(this, messenger);
|
|
@@ -28,7 +29,7 @@ export const recipientsCommand = (messenger) => {
|
|
|
28
29
|
else if (chats.length === 0)
|
|
29
30
|
renderer.note("the recipient list is on and empty — this profile may send nowhere");
|
|
30
31
|
});
|
|
31
|
-
grantsAuthority(annotate(command.command("add"), { effects: FETCH }))
|
|
32
|
+
grantsAuthority(annotate(command.command("add"), { effects: FETCH, examples: [`${tool} recipients add "Book club"`] }))
|
|
32
33
|
.argument("<chat>", messenger.chatArgument)
|
|
33
34
|
.description("allow sending to this chat; the first add turns the list on")
|
|
34
35
|
.action(async function (chat) {
|
|
@@ -43,7 +44,7 @@ export const recipientsCommand = (messenger) => {
|
|
|
43
44
|
});
|
|
44
45
|
renderAnswer(this, context, itemOf({ id: found.id, title: found.title, added }));
|
|
45
46
|
});
|
|
46
|
-
grantsAuthority(annotate(command.command("remove"), { effects: LOCAL_WRITE }))
|
|
47
|
+
grantsAuthority(annotate(command.command("remove"), { effects: LOCAL_WRITE, examples: [`${tool} recipients remove "Book club"`] }))
|
|
47
48
|
.argument("<chat>", "chat id, or the title as the list shows it")
|
|
48
49
|
.description("stop allowing this chat; the list stays on")
|
|
49
50
|
.action(async function (chat) {
|
|
@@ -54,7 +55,7 @@ export const recipientsCommand = (messenger) => {
|
|
|
54
55
|
throw new CliError("not_found", `${chat.trim()} is not on the recipient list of ${settings.profile}`);
|
|
55
56
|
renderAnswer(this, context, itemOf({ id: gone.id, title: gone.title, removed: true }));
|
|
56
57
|
});
|
|
57
|
-
grantsAuthority(annotate(command.command("clear"), { effects: LOCAL_WRITE }))
|
|
58
|
+
grantsAuthority(annotate(command.command("clear"), { effects: LOCAL_WRITE, examples: [`${tool} recipients clear`] }))
|
|
58
59
|
.description("delete the list, which turns it off: this profile may send to any chat again")
|
|
59
60
|
.action(async function () {
|
|
60
61
|
const context = messengerContext(this, messenger);
|
|
@@ -33,6 +33,7 @@ export const inboxCommand = (messenger) => annotate(new Command("inbox"), {
|
|
|
33
33
|
output: { entity: "message", view: "summary" },
|
|
34
34
|
query: INBOX_QUERY,
|
|
35
35
|
})
|
|
36
|
+
.summary("unread chats and their newest messages")
|
|
36
37
|
.description("other people's unread messages in every chat; --new for what arrived since the last check")
|
|
37
38
|
.addHelpText("after", queryHelp(INBOX_QUERY))
|
|
38
39
|
.argument("[query...]", "from when: date>2h, date>2026-10-01; the saved point stays put")
|
|
@@ -21,7 +21,9 @@ export const httpTokenFile = (app, profile, env) => join(resolvePaths({ appName:
|
|
|
21
21
|
const withFlags = (command) => command.option("--permission <key=level>", "make a permission stricter for this server only, like messages.send=deny; it never loosens the profile; repeat for more keys", (value, previous = []) => [...previous, value]);
|
|
22
22
|
export const mcpCommand = (messenger) => {
|
|
23
23
|
const { app } = messenger;
|
|
24
|
-
const command = withFlags(annotate(new Command("mcp"), { effects: REMOTE_WRITE, lifetime: "persistent", mcp: false })
|
|
24
|
+
const command = withFlags(annotate(new Command("mcp"), { effects: REMOTE_WRITE, lifetime: "persistent", mcp: false })
|
|
25
|
+
.summary("serve this profile's commands to an AI agent")
|
|
26
|
+
.description(`serve this profile to an agent over MCP, on stdin and stdout — \`claude mcp add ${app.command} -- ${app.command} <profile> mcp\``))
|
|
25
27
|
.option("--http", "serve over HTTP on 127.0.0.1 for ChatGPT and Claude in the browser, behind your tunnel; the profile's permissions decide")
|
|
26
28
|
.option("--port <port>", `the local port for --http (default ${DEFAULT_PORT})`)
|
|
27
29
|
.option("--public-url <url>", "the tunnel's https address the browser apps use, e.g. https://<name>.ts.net")
|
|
@@ -55,7 +57,9 @@ export const mcpCommand = (messenger) => {
|
|
|
55
57
|
command.addCommand(withFlags(annotate(new Command("config"), {
|
|
56
58
|
effects: LOCAL_READ,
|
|
57
59
|
examples: [`${app.command} personal mcp config`],
|
|
58
|
-
})
|
|
60
|
+
})
|
|
61
|
+
.summary("print this profile's local MCP configuration")
|
|
62
|
+
.description("print the mcpServers entry for Claude Desktop, Cursor and others, with full paths; writes nothing")).action(async function () {
|
|
59
63
|
const flags = this.optsWithGlobals();
|
|
60
64
|
const context = messengerContext(this, messenger);
|
|
61
65
|
const { settings, env } = context;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
import { annotate } from "@wirecat/cli-base/commands";
|
|
3
|
+
import { parseDuration } from "../../domain/duration.js";
|
|
4
|
+
import { refusedPlace } from "../../sends/upload.js";
|
|
5
|
+
import { FETCHING } from "../../services/archive.js";
|
|
6
|
+
import { archiveMedia, checkMediaArchive, validateMediaSince } from "../../services/media-archive.js";
|
|
7
|
+
import { environmentOf, setExitCode } from "../context.js";
|
|
8
|
+
import { FETCH, LOCAL_READ } from "../effects.js";
|
|
9
|
+
import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
|
|
10
|
+
import { messengerContext } from "./context.js";
|
|
11
|
+
import { addMediaArchiveMove } from "./media-archive-move-command.js";
|
|
12
|
+
import { addMediaArchiveTimers } from "./media-archive-timer-command.js";
|
|
13
|
+
import { stopOnSignal } from "./patience.js";
|
|
14
|
+
export const addMediaArchiveCommands = (archive, messenger) => {
|
|
15
|
+
annotate(archive.command("download"), {
|
|
16
|
+
effects: FETCH,
|
|
17
|
+
examples: [`${messenger.app.command} attachments download ./media-archive`],
|
|
18
|
+
})
|
|
19
|
+
.description("archive media from selected chats; verify and keep existing copies")
|
|
20
|
+
.addHelpText("after", `\nMedia is saved only when you request a download; serve does not save these copies.\nTo disable an installed daily backup: ${messenger.app.command} <profile> attachments jobs stop <dir>.`)
|
|
21
|
+
.argument("<dir>", "archive directory; created if missing")
|
|
22
|
+
.option("--chat <chat>", `only this chat; repeat for more; ${messenger.chatArgument}`, (value, previous) => [...previous, value], [])
|
|
23
|
+
.option("--since-time <time>", "include messages from this ISO timestamp with a time zone")
|
|
24
|
+
.option("--pause <duration>", "pause between history pages", messenger.fetching?.pause ?? FETCHING.pause)
|
|
25
|
+
.action(async function (directory) {
|
|
26
|
+
const context = messengerContext(this, messenger);
|
|
27
|
+
const options = this.opts();
|
|
28
|
+
validateMediaSince(options.sinceTime);
|
|
29
|
+
const pauseMs = parseDuration(options.pause, "--pause");
|
|
30
|
+
if (context.settings.offline)
|
|
31
|
+
throw new CliError("validation_error", "archiving media needs online mode");
|
|
32
|
+
if (refusedPlace(directory, messenger.app, context.env))
|
|
33
|
+
throw new CliError("validation_error", "the archive cannot write credential, application or message-store folders");
|
|
34
|
+
const stop = stopOnSignal(this);
|
|
35
|
+
const environment = environmentOf(this);
|
|
36
|
+
const signal = AbortSignal.any([
|
|
37
|
+
stop.signal,
|
|
38
|
+
...[environment.signal, environment.commandSignal].filter((one) => one !== undefined),
|
|
39
|
+
]);
|
|
40
|
+
try {
|
|
41
|
+
const result = await context.withServices((services) => archiveMedia(services, {
|
|
42
|
+
directory,
|
|
43
|
+
chats: options.chat,
|
|
44
|
+
sinceTime: options.sinceTime,
|
|
45
|
+
pauseMs,
|
|
46
|
+
signal,
|
|
47
|
+
pageSize: messenger.history === "store" ? 100 : (messenger.fetching ?? FETCHING).page,
|
|
48
|
+
orderByTime: messenger.history !== "store" && messenger.fetching?.orderBy === "time",
|
|
49
|
+
}));
|
|
50
|
+
renderAnswer(this, context, itemOf(snakeKeys(result)), () => `${result.saved} saved, ${result.existing} verified copies already present; ${result.chats} chats\n`);
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
stop.release();
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
annotate(archive.command("check"), {
|
|
57
|
+
effects: LOCAL_READ,
|
|
58
|
+
examples: [`${messenger.app.command} attachments check ./media-archive`],
|
|
59
|
+
})
|
|
60
|
+
.description("rehash all archived files without connecting to the messenger")
|
|
61
|
+
.argument("<dir>", "archive directory")
|
|
62
|
+
.action(async function (directory) {
|
|
63
|
+
const context = messengerContext(this, messenger);
|
|
64
|
+
const environment = environmentOf(this);
|
|
65
|
+
const result = await checkMediaArchive(directory, environment.commandSignal ?? environment.signal);
|
|
66
|
+
if (!result.valid)
|
|
67
|
+
setExitCode(this, 1);
|
|
68
|
+
renderAnswer(this, context, itemOf(snakeKeys(result)), () => `${result.checked} files checked; ${result.files.filter((one) => !one.valid).length} missing or changed\n`);
|
|
69
|
+
});
|
|
70
|
+
addMediaArchiveTimers(archive, messenger);
|
|
71
|
+
addMediaArchiveMove(archive, messenger);
|
|
72
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
import { annotate } from "@wirecat/cli-base/commands";
|
|
3
|
+
import { refusedPlace } from "../../sends/upload.js";
|
|
4
|
+
import { moveMediaArchive } from "../../services/media-archive-move.js";
|
|
5
|
+
import { environmentOf } from "../context.js";
|
|
6
|
+
import { LOCAL_WRITE } from "../effects.js";
|
|
7
|
+
import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
|
|
8
|
+
import { messengerContext, refuseLocalWrite } from "./context.js";
|
|
9
|
+
export const addMediaArchiveMove = (attachments, messenger) => {
|
|
10
|
+
annotate(attachments.command("move"), {
|
|
11
|
+
effects: LOCAL_WRITE,
|
|
12
|
+
mcp: false,
|
|
13
|
+
examples: [
|
|
14
|
+
`${messenger.app.command} attachments move ./media-archive ./older-images --before-time 2026-10-01T00:00:00Z --dry-run`,
|
|
15
|
+
],
|
|
16
|
+
})
|
|
17
|
+
.description("move older images into another verified archive; remember their new locations")
|
|
18
|
+
.argument("<dir>", "source media archive directory")
|
|
19
|
+
.argument("<archive-dir>", "destination archive directory; created if needed")
|
|
20
|
+
.requiredOption("--before-time <time>", "move images whose message date is before this ISO timestamp with zone")
|
|
21
|
+
.option("--dry-run", "show counts and bytes without moving files or creating the destination")
|
|
22
|
+
.addHelpText("after", "\nCopies are verified at the destination before local image bytes are removed.\nSource manifests retain relocation records; checks follow them and downloads do not fetch them again.\nImages without a recorded message date are left in place and counted as undated.")
|
|
23
|
+
.action(async function (directory, archiveDirectory) {
|
|
24
|
+
const context = messengerContext(this, messenger);
|
|
25
|
+
const options = this.opts();
|
|
26
|
+
if (!options.dryRun)
|
|
27
|
+
refuseLocalWrite(context, messenger.app.command, "attachments.move");
|
|
28
|
+
if ([directory, archiveDirectory].some((folder) => refusedPlace(folder, messenger.app, context.env)))
|
|
29
|
+
throw new CliError("validation_error", "media archives cannot use credential, application or message-store folders");
|
|
30
|
+
const environment = environmentOf(this);
|
|
31
|
+
const result = await moveMediaArchive({
|
|
32
|
+
directory,
|
|
33
|
+
archiveDirectory,
|
|
34
|
+
beforeTime: options.beforeTime,
|
|
35
|
+
dryRun: options.dryRun,
|
|
36
|
+
signal: environment.commandSignal ?? environment.signal,
|
|
37
|
+
});
|
|
38
|
+
renderAnswer(this, context, itemOf(snakeKeys(result)), () => `${result.dryRun ? `${result.selected} images selected (${result.selectedBytes} bytes)` : `${result.moved} images moved (${result.movedBytes} bytes)`}; ${result.undated} undated images left in place\n`);
|
|
39
|
+
});
|
|
40
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { realpath } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { CliError } from "@wirecat/cli-base";
|
|
4
|
+
import { annotate } from "@wirecat/cli-base/commands";
|
|
5
|
+
import { dailyArchiveTimer, manageDailyArchiveTimer, runArchiveTimerCommand, } from "../../background/daily-archive-timer.js";
|
|
6
|
+
import { thisMachine } from "../../background/system.js";
|
|
7
|
+
import { refusedPlace } from "../../sends/upload.js";
|
|
8
|
+
import { validateMediaSince } from "../../services/media-archive.js";
|
|
9
|
+
import { environmentOf } from "../context.js";
|
|
10
|
+
import { grantsAuthority, LOCAL_READ, LOCAL_WRITE } from "../effects.js";
|
|
11
|
+
import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
|
|
12
|
+
import { messengerContext } from "./context.js";
|
|
13
|
+
export const addMediaArchiveTimers = (attachments, messenger) => {
|
|
14
|
+
const archive = annotate(attachments.command("jobs"), { mcp: false }).description("daily media archive timers for this profile");
|
|
15
|
+
for (const action of ["install", "start", "stop", "status", "uninstall"]) {
|
|
16
|
+
const command = annotate(archive.command(action), {
|
|
17
|
+
effects: action === "status" ? LOCAL_READ : LOCAL_WRITE,
|
|
18
|
+
mcp: false,
|
|
19
|
+
examples: [`${messenger.app.command} attachments jobs ${action} ./media-archive`],
|
|
20
|
+
})
|
|
21
|
+
.description({
|
|
22
|
+
install: "install a Linux daily archive timer for this directory; starts nothing",
|
|
23
|
+
start: "enable the installed archive timer at login and start its daily schedule",
|
|
24
|
+
stop: "disable the archive timer; a download already running finishes",
|
|
25
|
+
status: "show whether this directory's archive timer is installed and active",
|
|
26
|
+
uninstall: "remove this directory's stopped archive timer; keep archived files",
|
|
27
|
+
}[action])
|
|
28
|
+
.argument("<dir>", "archive directory, used with the same profile as install");
|
|
29
|
+
if (action !== "status")
|
|
30
|
+
grantsAuthority(command);
|
|
31
|
+
if (action === "install")
|
|
32
|
+
command
|
|
33
|
+
.option("--at <HH:mm>", "daily start time", "03:00")
|
|
34
|
+
.option("--timezone <zone>", "IANA time zone of the daily start time", "UTC")
|
|
35
|
+
.option("--chat <chat>", `only this chat; repeat for more; ${messenger.chatArgument}`, (value, previous) => [...previous, value], [])
|
|
36
|
+
.option("--since-time <time>", "include messages from this ISO timestamp with a time zone");
|
|
37
|
+
command.action(async function (directory) {
|
|
38
|
+
const context = messengerContext(this, messenger);
|
|
39
|
+
const environment = environmentOf(this);
|
|
40
|
+
const signal = environment.commandSignal ?? environment.signal;
|
|
41
|
+
const system = environment.system ?? {
|
|
42
|
+
...thisMachine(),
|
|
43
|
+
run: (args, env) => runArchiveTimerCommand(args, env, signal),
|
|
44
|
+
};
|
|
45
|
+
const options = this.opts();
|
|
46
|
+
validateMediaSince(options.sinceTime);
|
|
47
|
+
if (action === "install" && refusedPlace(directory, messenger.app, context.env))
|
|
48
|
+
throw new CliError("validation_error", "the archive cannot write credential, application or message-store folders");
|
|
49
|
+
// Canonicalize existing destinations so a symlink alias addresses the same timer.
|
|
50
|
+
const root = await realpath(directory).catch((error) => {
|
|
51
|
+
if (error.code !== "ENOENT")
|
|
52
|
+
throw error;
|
|
53
|
+
return resolve(directory);
|
|
54
|
+
});
|
|
55
|
+
const plan = dailyArchiveTimer({
|
|
56
|
+
app: messenger.app,
|
|
57
|
+
profile: context.profile,
|
|
58
|
+
directory: root,
|
|
59
|
+
entry: system.entry,
|
|
60
|
+
env: context.env,
|
|
61
|
+
chats: options.chat,
|
|
62
|
+
sinceTime: options.sinceTime,
|
|
63
|
+
at: options.at,
|
|
64
|
+
timeZone: options.timezone,
|
|
65
|
+
});
|
|
66
|
+
const result = await manageDailyArchiveTimer(plan, action, system, context.env, environment.commandSignal ?? environment.signal);
|
|
67
|
+
renderAnswer(this, context, itemOf(snakeKeys(result)), () => `${result.name}: ${action}\n`);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
};
|
|
@@ -31,6 +31,11 @@ export declare const threadAnswer: ({ items, ...thread }: ThreadContext, reader:
|
|
|
31
31
|
excerpt?: string | undefined;
|
|
32
32
|
matched_in?: "context" | "file" | "filename" | "picture" | "text" | "transcript" | undefined;
|
|
33
33
|
score?: number | undefined;
|
|
34
|
+
match?: "beginnings" | "corrected" | "words" | undefined;
|
|
35
|
+
corrections?: {
|
|
36
|
+
from: string;
|
|
37
|
+
to: string[];
|
|
38
|
+
}[] | undefined;
|
|
34
39
|
found_in?: "archive" | "both" | "server" | undefined;
|
|
35
40
|
discovery?: {
|
|
36
41
|
coverage: number;
|
|
@@ -33,6 +33,11 @@ export declare const writtenAnswer: ({ operationId, sendId, message, scheduledFo
|
|
|
33
33
|
excerpt?: string | undefined;
|
|
34
34
|
matched_in?: "context" | "file" | "filename" | "picture" | "text" | "transcript" | undefined;
|
|
35
35
|
score?: number | undefined;
|
|
36
|
+
match?: "beginnings" | "corrected" | "words" | undefined;
|
|
37
|
+
corrections?: {
|
|
38
|
+
from: string;
|
|
39
|
+
to: string[];
|
|
40
|
+
}[] | undefined;
|
|
36
41
|
found_in?: "archive" | "both" | "server" | undefined;
|
|
37
42
|
discovery?: {
|
|
38
43
|
coverage: number;
|
|
@@ -14,6 +14,7 @@ export const commentsCommand = (messenger) => {
|
|
|
14
14
|
examples: [`${messenger.app.command} personal messages comments "News channel" 101 --limit 20`],
|
|
15
15
|
effects: REMOTE_READ,
|
|
16
16
|
})
|
|
17
|
+
.summary("comments on a channel post")
|
|
17
18
|
.description("the comments under a channel post, oldest to newest; they live in its discussion group")
|
|
18
19
|
.addHelpText("after", queryHelp(COMMENTS_QUERY))
|
|
19
20
|
.argument("<chat>", `the channel: ${messenger.chatArgument}`)
|
|
@@ -17,7 +17,7 @@ export const deleteCommand = (messenger) => annotate(new Command("delete"), {
|
|
|
17
17
|
})
|
|
18
18
|
.description("delete messages for you only; with --for-everyone, for everyone in the chat")
|
|
19
19
|
.argument("<chat>", messenger.chatArgument)
|
|
20
|
-
.argument("<
|
|
20
|
+
.argument("<message...>", `the message ids, by spaces or commas, at most ${DELETE_AT_ONCE}`)
|
|
21
21
|
.addOption(ownerOnlyOption("--for-everyone", "delete for everyone in the chat, not only for you — they cannot get it back"))
|
|
22
22
|
.addOption(ownerOnlyOption("--allow-dangerous", "go ahead without the question an ask level puts before a deletion"))
|
|
23
23
|
.action(async function (chat, messages) {
|
|
@@ -26,6 +26,7 @@ export const messagesSearchCommand = (messenger) => {
|
|
|
26
26
|
});
|
|
27
27
|
const syncing = syncsFirst(messenger) ? "; optionally fetches new messages with --sync-first" : "";
|
|
28
28
|
return command
|
|
29
|
+
.summary("find messages by words and filters")
|
|
29
30
|
.description(messenger.serverSearch
|
|
30
31
|
? `search messenger messages in the local store and on the messenger's server (--backend)${syncing}; --discover searches the archive only`
|
|
31
32
|
: `search messenger messages in the local store — what was read, fetched or kept by serve${syncing}; --discover searches the archive only`)
|
|
@@ -9,6 +9,7 @@ import { syncOptions, syncRequest, syncsFirst } from "./search-sync-options.js";
|
|
|
9
9
|
import { countsAnswer, refuseJsonl } from "./stats-answers.js";
|
|
10
10
|
const GROUPINGS = ["chat", "sender", "day", "hour"];
|
|
11
11
|
export const messagesStatsCommand = (messenger) => syncOptions(annotate(new Command("show"), { effects: REMOTE_READ, output: { entity: "message_counts" } }), messenger)
|
|
12
|
+
.summary("count stored messages by chat, sender, day or hour")
|
|
12
13
|
.description(`how many stored messages match, by chat, sender, day or hour — the local store only${syncsFirst(messenger) ? "; optionally fetches new messages with --sync-first" : ""}`)
|
|
13
14
|
.argument("[query...]", "a strict Lucene query, as for search messages; none counts every stored message; with --saved, more words AND-ed to it")
|
|
14
15
|
.option("--by <chat|sender|day|hour>", "what to count by (default: chat)", groupingOf)
|
|
@@ -61,6 +61,7 @@ export const modelsCommand = (messenger) => {
|
|
|
61
61
|
const models = annotate(new Command("models"), { mcp: false }).description("models that run on this machine");
|
|
62
62
|
const audio = models.command("audio").description("speech models for transcribing voice messages");
|
|
63
63
|
withQuery(annotate(audio.command("list"), { effects: LOCAL_READ, examples: [`${tool} models audio list`] }), MODELS_QUERY)
|
|
64
|
+
.summary("speech models and their download status")
|
|
64
65
|
.description("the speech models, most suitable first, which are downloaded, and which one is the default")
|
|
65
66
|
.action(async function (words) {
|
|
66
67
|
const context = baseContext(this, messenger.resolveSettings);
|
|
@@ -107,6 +108,7 @@ export const modelsCommand = (messenger) => {
|
|
|
107
108
|
});
|
|
108
109
|
const text = models.command("text").description("embedding models for searching conversations by meaning");
|
|
109
110
|
withQuery(annotate(text.command("list"), { effects: LOCAL_READ, examples: [`${tool} models text list`] }), MODELS_QUERY)
|
|
111
|
+
.summary("embedding models and their download status")
|
|
110
112
|
.description("the embedding models, most suitable first, which are downloaded, and which one is the default")
|
|
111
113
|
.action(async function (words) {
|
|
112
114
|
const context = baseContext(this, messenger.resolveSettings);
|
|
@@ -127,6 +129,7 @@ export const modelsCommand = (messenger) => {
|
|
|
127
129
|
}))
|
|
128
130
|
.argument("<model>", "a model id from `models text list`")
|
|
129
131
|
.addOption(new Option("--accept-terms", "accept the model's licence terms, for a model that has its own"))
|
|
132
|
+
.summary("download and verify an embedding model")
|
|
130
133
|
.description("download an embedding model once, checked against the sha256 this version expects")
|
|
131
134
|
.action(async function (id, options) {
|
|
132
135
|
const context = baseContext(this, messenger.resolveSettings);
|