@wirecat/cli-core 0.397.0 → 0.399.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 +65 -0
- package/README.md +12 -9
- package/dist/cli/analysis-choice.d.ts +1 -0
- package/dist/cli/analysis-choice.js +3 -0
- package/dist/cli/bot/admins.js +14 -4
- package/dist/cli/bot/command.js +38 -16
- package/dist/cli/bot/examples.d.ts +3 -0
- package/dist/cli/bot/examples.js +2 -0
- package/dist/cli/bot/mcp.js +4 -1
- package/dist/cli/bot/messages.js +17 -6
- package/dist/cli/bot/moderation.js +16 -4
- package/dist/cli/bot/people.js +16 -4
- package/dist/cli/bot/setup.js +27 -8
- package/dist/cli/bot/watch.js +7 -1
- package/dist/cli/commands-command.js +1 -1
- package/dist/cli/config-command.js +1 -1
- package/dist/cli/embedding-choice.d.ts +2 -1
- package/dist/cli/embedding-choice.js +4 -1
- package/dist/cli/guide.js +0 -1
- package/dist/cli/help/command-help.d.ts +9 -2
- package/dist/cli/help/command-help.js +22 -4
- package/dist/cli/help/first-screen.js +3 -3
- package/dist/cli/messenger/account-command.js +2 -1
- package/dist/cli/messenger/admin-commands.js +18 -9
- package/dist/cli/messenger/archive-commands.js +2 -2
- package/dist/cli/messenger/backfill-command.js +3 -3
- package/dist/cli/messenger/catch-up-answers.d.ts +4 -1
- package/dist/cli/messenger/catch-up-answers.js +11 -3
- package/dist/cli/messenger/chats-command.js +2 -1
- package/dist/cli/messenger/chats-stats-command.js +1 -1
- package/dist/cli/messenger/complete-command.js +5 -1
- package/dist/cli/messenger/contacts-command.js +4 -3
- package/dist/cli/messenger/context.d.ts +7 -0
- package/dist/cli/messenger/conversations-command.js +27 -13
- package/dist/cli/messenger/doctor-command.js +2 -2
- package/dist/cli/messenger/flood-command.js +1 -1
- package/dist/cli/messenger/hearing-command.d.ts +1 -1
- package/dist/cli/messenger/hearing-command.js +1 -1
- package/dist/cli/messenger/inbox.js +7 -4
- package/dist/cli/messenger/lacks.d.ts +7 -0
- package/dist/cli/messenger/lacks.js +18 -1
- package/dist/cli/messenger/media-archive-move-command.js +0 -1
- package/dist/cli/messenger/media-archive-timer-command.js +2 -2
- package/dist/cli/messenger/messages-command.js +2 -1
- package/dist/cli/messenger/messages-stats-command.js +1 -1
- package/dist/cli/messenger/metadata-command.js +1 -1
- package/dist/cli/messenger/models-command.js +5 -2
- package/dist/cli/messenger/private-people-command.js +5 -3
- package/dist/cli/messenger/replies-command.js +1 -1
- package/dist/cli/messenger/replies-edit-command.d.ts +1 -1
- package/dist/cli/messenger/replies-edit-command.js +13 -5
- package/dist/cli/messenger/retention-command.js +1 -1
- package/dist/cli/messenger/review.js +13 -6
- package/dist/cli/messenger/schedule-command.js +3 -3
- package/dist/cli/messenger/search-command.js +2 -2
- package/dist/cli/messenger/searches-command.js +1 -1
- package/dist/cli/messenger/serve-command.js +5 -1
- package/dist/cli/messenger/serve-relay.d.ts +2 -0
- package/dist/cli/messenger/server-command.js +12 -3
- package/dist/cli/messenger/stats-command.js +1 -1
- package/dist/cli/messenger/store-maintenance-command.js +3 -3
- package/dist/cli/messenger/tags-command.js +11 -5
- package/dist/cli/messenger/transcribe-command.js +1 -1
- package/dist/cli/messenger/watch-command.js +5 -1
- package/dist/cli/runs/command.js +2 -3
- package/dist/cli/skill-command.js +1 -1
- package/dist/domain/models.d.ts +2 -0
- package/dist/mcp/prompts.js +8 -2
- package/dist/services/deps.d.ts +1 -1
- package/dist/services/deps.js +1 -1
- package/dist/services/inbox.js +5 -5
- package/dist/services/search-kind.js +1 -1
- package/package.json +4 -4
- package/parity.json +134 -234
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CliError } from "@wirecat/cli-base";
|
|
2
2
|
import { annotate } from "@wirecat/cli-base/commands";
|
|
3
|
-
import { Command } from "commander";
|
|
3
|
+
import { Command, Option } from "commander";
|
|
4
4
|
import { analysisConsents } from "../../analysis/consents.js";
|
|
5
5
|
import { openAnalysis, providerIdentity } from "../../analysis/provider.js";
|
|
6
6
|
import { runAnalysis } from "../../analysis/runner.js";
|
|
@@ -39,7 +39,10 @@ export const conversationsCommand = (messenger) => {
|
|
|
39
39
|
"chat that changed since its build and every group never built; never asks the messenger")
|
|
40
40
|
.option("--chat <chat>", messenger.chatArgument)
|
|
41
41
|
.option("--analyze", "link batches using the configured analysis provider; requires --chat and remembers consent for this chat/provider")
|
|
42
|
-
.
|
|
42
|
+
.addOption(new Option("--analysis-provider <provider>", "analysis: agent, openai or anthropic").conflicts("provider"))
|
|
43
|
+
.addOption(new Option("--provider <provider>", "compatibility alias for --analysis-provider")
|
|
44
|
+
.hideHelp()
|
|
45
|
+
.conflicts("analysisProvider"))
|
|
43
46
|
.option("--model <model>", "analysis model; overrides analysis_model")
|
|
44
47
|
.option("--base-url <url>", "analysis API endpoint; overrides analysis_base_url")
|
|
45
48
|
.option("--size <n>", "analysis answer messages per batch, 10–200; default 50", positiveCount("--size"))
|
|
@@ -95,7 +98,8 @@ export const conversationsCommand = (messenger) => {
|
|
|
95
98
|
renderAnswer(this, context, itemOf(snakeKeys(result)));
|
|
96
99
|
return;
|
|
97
100
|
}
|
|
98
|
-
if (options.
|
|
101
|
+
if (options.analysisProvider !== undefined ||
|
|
102
|
+
options.provider !== undefined ||
|
|
99
103
|
options.model !== undefined ||
|
|
100
104
|
options.baseUrl !== undefined ||
|
|
101
105
|
options.size !== undefined ||
|
|
@@ -247,18 +251,19 @@ export const conversationsCommand = (messenger) => {
|
|
|
247
251
|
.summary("remove an agent's saved conversation-link answers")
|
|
248
252
|
.description("drop your agent's answers for a chat, or only one model's; messages are never touched")
|
|
249
253
|
.requiredOption("--chat <chat>", messenger.chatArgument)
|
|
250
|
-
.
|
|
254
|
+
.addOption(new Option("--answer-model <model>", "only the stored answers this model gave").conflicts("model"))
|
|
255
|
+
.addOption(new Option("--model <model>", "compatibility alias for --answer-model").hideHelp().conflicts("answerModel"))
|
|
251
256
|
.action(async function () {
|
|
252
|
-
const { chat, model } = this.opts();
|
|
257
|
+
const { chat, model, answerModel } = this.opts();
|
|
253
258
|
const context = messengerContext(this, messenger);
|
|
254
259
|
refuseLocalWrite(context, messenger.app.command, LINKS_KEY);
|
|
255
|
-
const cleared = await context.withServices((services) => services.conversations.clearAnswers(chat, model));
|
|
260
|
+
const cleared = await context.withServices((services) => services.conversations.clearAnswers(chat, answerModel ?? model));
|
|
256
261
|
renderAnswer(this, context, itemOf(cleared), () => `${cleared.cleared} answers dropped\n`);
|
|
257
262
|
});
|
|
258
263
|
const embed = withModelOptions(reachesOtherService(annotate(new Command("embed"), { effects: REMOTE_WRITE }))
|
|
259
264
|
.summary("prepare conversation vectors for meaning search")
|
|
260
265
|
.description("compute a vector for each chunk of a chat's conversations for search by meaning — on this machine, or " +
|
|
261
|
-
"with --provider through a service and your key; resumes where it stopped; without --chat, every " +
|
|
266
|
+
"with --embedding-provider through a service and your key; resumes where it stopped; without --chat, every " +
|
|
262
267
|
"built chat with chunks left, on this machine only")
|
|
263
268
|
.option("--chat <chat>", messenger.chatArgument))
|
|
264
269
|
.option("--threads <n>", "local: threads in all (default: min(8, cores))", positiveCount("--threads"))
|
|
@@ -324,7 +329,10 @@ export const conversationsCommand = (messenger) => {
|
|
|
324
329
|
const chat = chatOf(options);
|
|
325
330
|
const context = messengerContext(this, messenger);
|
|
326
331
|
refuseLocalWrite(context, messenger.app.command, EMBED_KEY);
|
|
327
|
-
const given = options.model !== undefined ||
|
|
332
|
+
const given = options.model !== undefined ||
|
|
333
|
+
options.embeddingProvider !== undefined ||
|
|
334
|
+
options.provider !== undefined ||
|
|
335
|
+
options.baseUrl !== undefined;
|
|
328
336
|
const model = given ? choiceOf(options, messenger, context, { needKey: false }) : undefined;
|
|
329
337
|
const cleared = await context.withServices((services) => services.embeddings.clear(chat, model));
|
|
330
338
|
renderAnswer(this, context, itemOf(cleared), () => `${cleared.cleared} vectors dropped\n`);
|
|
@@ -365,16 +373,19 @@ export const conversationsCommand = (messenger) => {
|
|
|
365
373
|
annotate(consents.command("revoke"), { effects: LOCAL_WRITE })
|
|
366
374
|
.description("forget analysis permissions so the next analysis asks again")
|
|
367
375
|
.option("--chat <chat>", "revoke only this chat's consents; defaults to every chat")
|
|
368
|
-
.
|
|
376
|
+
.addOption(new Option("--consent-provider <identity>", "exact provider identity from consents list; defaults to every provider").conflicts("provider"))
|
|
377
|
+
.addOption(new Option("--provider <identity>", "compatibility alias for --consent-provider")
|
|
378
|
+
.hideHelp()
|
|
379
|
+
.conflicts("consentProvider"))
|
|
369
380
|
.action(async function () {
|
|
370
|
-
const { chat, provider } = this.opts();
|
|
381
|
+
const { chat, provider, consentProvider } = this.opts();
|
|
371
382
|
const context = messengerContext(this, messenger);
|
|
372
383
|
refuseLocalWrite(context, messenger.app.command, LINKS_KEY);
|
|
373
384
|
await context.withStore(async (store, account) => {
|
|
374
385
|
const chatId = chat === undefined
|
|
375
386
|
? undefined
|
|
376
387
|
: (await servicesFor(storedDeps(messenger, store, account, context.guard)).conversations.batchStatus(chat, BATCH_SIZE.default)).chat;
|
|
377
|
-
await analysisConsents(store, account).revoke(chatId, provider);
|
|
388
|
+
await analysisConsents(store, account).revoke(chatId, consentProvider ?? provider);
|
|
378
389
|
});
|
|
379
390
|
renderAnswer(this, context, itemOf({ revoked: true }));
|
|
380
391
|
});
|
|
@@ -393,7 +404,10 @@ const chatOf = ({ chat }) => {
|
|
|
393
404
|
};
|
|
394
405
|
const withModelOptions = (command) => command
|
|
395
406
|
.option("--model <model>", "local: a model id from `models text list` (default: e5-small); remote: the provider's model")
|
|
396
|
-
.
|
|
407
|
+
.addOption(new Option("--embedding-provider <provider>", "embedding provider: local or openai; flags override profile settings").conflicts("provider"))
|
|
408
|
+
.addOption(new Option("--provider <provider>", "compatibility alias for --embedding-provider")
|
|
409
|
+
.hideHelp()
|
|
410
|
+
.conflicts("embeddingProvider"))
|
|
397
411
|
.option("--base-url <url>", "a server with OpenAI's /v1/embeddings: Gemini, Jina, or Ollama and LM Studio on this machine")
|
|
398
412
|
.option("--dims <n>", "remote: the vector size — needed with --base-url; shortens an OpenAI model's", positiveCount("--dims"));
|
|
399
413
|
/** How much one run over many chats may do: the owner's machine, not a background job, pays for it. */
|
|
@@ -412,7 +426,7 @@ const bounds = ({ maxChats, maxChunks }) => ({
|
|
|
412
426
|
/** Work nobody named chat by chat never sends chat text off the machine. */
|
|
413
427
|
const localOnly = (model, what) => {
|
|
414
428
|
if (typeof model !== "string") {
|
|
415
|
-
throw new CliError("validation_error", `${what} uses the model on this machine only — drop --provider and --base-url, or embed one chat with --chat`);
|
|
429
|
+
throw new CliError("validation_error", `${what} uses the model on this machine only — drop --embedding-provider and --base-url, or embed one chat with --chat`);
|
|
416
430
|
}
|
|
417
431
|
};
|
|
418
432
|
const renderRefreshed = (command, context, refreshed) => {
|
|
@@ -57,7 +57,7 @@ const INCLUDES = [
|
|
|
57
57
|
const EXCLUDES = ["message text", "chat titles", "names", "phone numbers", "the session", "the app credentials"];
|
|
58
58
|
const reportCommand = (messenger) => {
|
|
59
59
|
const { app } = messenger;
|
|
60
|
-
const report = annotate(new Command("report"), { effects: REMOTE_READ
|
|
60
|
+
const report = annotate(new Command("report"), { effects: REMOTE_READ })
|
|
61
61
|
.description("what a problem report holds; writes nothing")
|
|
62
62
|
.action(function () {
|
|
63
63
|
const output = outputFor(this);
|
|
@@ -74,7 +74,7 @@ const reportCommand = (messenger) => {
|
|
|
74
74
|
`About a given run: ${create} --run <id> (ids: ${lineOf(this, "runs list")})`,
|
|
75
75
|
].join("\n"));
|
|
76
76
|
});
|
|
77
|
-
destroysNothing(annotate(report.command("create"), { effects: LOCAL_WRITE }))
|
|
77
|
+
destroysNothing(annotate(report.command("create"), { effects: LOCAL_WRITE, mcp: false }))
|
|
78
78
|
.description("write a problem report to a file, and say where to send it")
|
|
79
79
|
.option("--run <id>", "the run the report is about; the newest failed one if not given")
|
|
80
80
|
.option("--output <file>", "where to write it; a new file in this directory if not given")
|
|
@@ -14,7 +14,7 @@ export const floodCommand = (messenger) => {
|
|
|
14
14
|
const name = messenger.name ?? app.command;
|
|
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
|
-
.addCommand(grantsAuthority(annotate(new Command("clear"), { effects: LOCAL_WRITE }))
|
|
17
|
+
.addCommand(grantsAuthority(annotate(new Command("clear"), { effects: LOCAL_WRITE, examples: [`${app.command} personal flood clear`] }))
|
|
18
18
|
.summary("clear a profile's stored rate-limit hold")
|
|
19
19
|
.description(`forget them, lift the hold and the profile's pace, once ${name} no longer limits the account; changes nothing there`)
|
|
20
20
|
.action(async function () {
|
|
@@ -2,7 +2,7 @@ import type { Message } from "../../domain/models.js";
|
|
|
2
2
|
import { type Hearing } from "../../speech/hearing.js";
|
|
3
3
|
import type { Messenger, MessengerContext, ReadConnection } from "./context.js";
|
|
4
4
|
export declare const TRANSCRIBE_OPTION: readonly ["--transcribe", "turn voice messages not heard yet into text — by the messenger where it can, else by a model on this machine; can take minutes"];
|
|
5
|
-
export declare const MODEL_OPTION: readonly ["--model <
|
|
5
|
+
export declare const MODEL_OPTION: readonly ["--model <model>", "which downloaded speech model hears them, with --transcribe; `models audio list` shows them"];
|
|
6
6
|
/**
|
|
7
7
|
* Kept transcripts on every read; new ones with `--transcribe`, using the read's connection when
|
|
8
8
|
* supplied. It is released before local recognition. `undefined` when there is no voice message, and then no file is opened.
|
|
@@ -5,7 +5,7 @@ export const TRANSCRIBE_OPTION = [
|
|
|
5
5
|
"turn voice messages not heard yet into text — by the messenger where it can, else by a model on this machine; can take minutes",
|
|
6
6
|
];
|
|
7
7
|
export const MODEL_OPTION = [
|
|
8
|
-
"--model <
|
|
8
|
+
"--model <model>",
|
|
9
9
|
"which downloaded speech model hears them, with --transcribe; `models audio list` shows them",
|
|
10
10
|
];
|
|
11
11
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CliError } from "@wirecat/cli-base";
|
|
2
2
|
import { annotate } from "@wirecat/cli-base/commands";
|
|
3
|
-
import { Command } from "commander";
|
|
3
|
+
import { Command, Option } from "commander";
|
|
4
4
|
import { renderMessages } from "../../render/messages.js";
|
|
5
5
|
import { CHAT_KINDS } from "../../services/chats.js";
|
|
6
6
|
import { kindsOf } from "../../services/inbox.js";
|
|
@@ -29,6 +29,7 @@ export const PARTIAL_NOTE = "the messenger did not list every chat, and did not
|
|
|
29
29
|
* `date>2h` is a one-off look from a time of your choosing and leaves the points where they were.
|
|
30
30
|
*/
|
|
31
31
|
export const inboxCommand = (messenger) => annotate(new Command("inbox"), {
|
|
32
|
+
examples: [`${messenger.app.command} personal inbox --limit 10`],
|
|
32
33
|
effects: REMOTE_READ,
|
|
33
34
|
output: { entity: "message", view: "summary" },
|
|
34
35
|
query: INBOX_QUERY,
|
|
@@ -40,7 +41,7 @@ export const inboxCommand = (messenger) => annotate(new Command("inbox"), {
|
|
|
40
41
|
.option("--new", "what arrived since the last check, each message once — for scheduled runs")
|
|
41
42
|
.option("--limit <n>", "at most this many per chat, the newest", positiveCount("--limit"))
|
|
42
43
|
.option("--all", "muted and archived chats too — left out unless they mention you or reply to you")
|
|
43
|
-
.
|
|
44
|
+
.addOption(new Option("--kind <kinds>", `compatibility spelling for kind:; ${CHAT_KINDS.join(", ")}`).hideHelp())
|
|
44
45
|
.option(...TRANSCRIBE_OPTION)
|
|
45
46
|
.option(...MODEL_OPTION)
|
|
46
47
|
.addHelpText("after", "To mark a chat read, use `chats mark-read <chat>`. The other side sees it.")
|
|
@@ -50,8 +51,10 @@ export const inboxCommand = (messenger) => annotate(new Command("inbox"), {
|
|
|
50
51
|
const context = messengerContext(this, messenger);
|
|
51
52
|
const { app } = messenger;
|
|
52
53
|
const { settings, renderer, format, env } = context;
|
|
53
|
-
const
|
|
54
|
-
|
|
54
|
+
const { since, meta, kinds: queriedKinds } = catchUpQuery(query, INBOX_QUERY, "inbox");
|
|
55
|
+
if (kind !== undefined && queriedKinds !== undefined)
|
|
56
|
+
throw new CliError("validation_error", "give kind: or its legacy --kind spelling, not both");
|
|
57
|
+
const kinds = queriedKinds ?? (kind === undefined ? undefined : kindsOf(kind));
|
|
55
58
|
if (fresh && since !== undefined)
|
|
56
59
|
throw new CliError("validation_error", "--new keeps its own point — not with a date in the query");
|
|
57
60
|
const saved = fresh && since === undefined
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
1
2
|
import type { MessengerAdapter } from "../../ports/messenger.js";
|
|
2
3
|
type Need = readonly [method: keyof MessengerAdapter, what: string];
|
|
3
4
|
/**
|
|
@@ -9,10 +10,16 @@ export declare const COMMAND_NEEDS: Readonly<Record<string, Need>>;
|
|
|
9
10
|
interface Lacking {
|
|
10
11
|
channelComments?: boolean;
|
|
11
12
|
lacks?: readonly (keyof MessengerAdapter)[];
|
|
13
|
+
never?: readonly (keyof MessengerAdapter)[];
|
|
12
14
|
history?: "server" | "store";
|
|
13
15
|
}
|
|
14
16
|
/** The one method this command needs and this messenger never has; none when it has it or the store answers. */
|
|
15
17
|
export declare const lackedNeed: (messenger: Lacking | undefined, path: string) => Need | undefined;
|
|
16
18
|
/** Refuses, before a connection, a command whose one method this messenger never has. */
|
|
17
19
|
export declare const refuseLacking: (messenger: Lacking | undefined, path: readonly string[]) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Hides under `group` each command whose one method this messenger can `never` have, from help, `commands` and MCP;
|
|
22
|
+
* typed anyway, it still refuses. A method it only `lacks` keeps its command in sight, since it may come.
|
|
23
|
+
*/
|
|
24
|
+
export declare const hideNever: <T extends Command>(messenger: Lacking, group: T) => T;
|
|
18
25
|
export {};
|
|
@@ -32,7 +32,7 @@ export const lackedNeed = (messenger, path) => {
|
|
|
32
32
|
return undefined;
|
|
33
33
|
if (path === "messages comments" && messenger?.channelComments === false)
|
|
34
34
|
return need;
|
|
35
|
-
if (!messenger?.lacks?.includes(need[0]))
|
|
35
|
+
if (!messenger?.lacks?.includes(need[0]) && !messenger?.never?.includes(need[0]))
|
|
36
36
|
return undefined;
|
|
37
37
|
return messenger.history === "store" && STORE_ANSWERS.has(path) ? undefined : need;
|
|
38
38
|
};
|
|
@@ -42,3 +42,20 @@ export const refuseLacking = (messenger, path) => {
|
|
|
42
42
|
if (need)
|
|
43
43
|
throw new CliError("validation_error", `this messenger cannot ${need[1]}`);
|
|
44
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Hides under `group` each command whose one method this messenger can `never` have, from help, `commands` and MCP;
|
|
47
|
+
* typed anyway, it still refuses. A method it only `lacks` keeps its command in sight, since it may come.
|
|
48
|
+
*/
|
|
49
|
+
export const hideNever = (messenger, group) => {
|
|
50
|
+
const walk = (command, path) => {
|
|
51
|
+
for (const child of command.commands) {
|
|
52
|
+
const at = [...path, child.name()];
|
|
53
|
+
const need = COMMAND_NEEDS[at.join(" ")];
|
|
54
|
+
if (need && messenger.never?.includes(need[0]))
|
|
55
|
+
child._hidden = true;
|
|
56
|
+
walk(child, at);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
walk(group, [group.name()]);
|
|
60
|
+
return group;
|
|
61
|
+
};
|
|
@@ -9,7 +9,6 @@ import { messengerContext, refuseLocalWrite } from "./context.js";
|
|
|
9
9
|
export const addMediaArchiveMove = (attachments, messenger) => {
|
|
10
10
|
annotate(attachments.command("move"), {
|
|
11
11
|
effects: LOCAL_WRITE,
|
|
12
|
-
mcp: false,
|
|
13
12
|
examples: [
|
|
14
13
|
`${messenger.app.command} attachments move ./media-archive ./older-images --before-time 2026-10-01T00:00:00Z --dry-run`,
|
|
15
14
|
],
|
|
@@ -11,11 +11,11 @@ import { grantsAuthority, LOCAL_READ, LOCAL_WRITE } from "../effects.js";
|
|
|
11
11
|
import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
|
|
12
12
|
import { messengerContext } from "./context.js";
|
|
13
13
|
export const addMediaArchiveTimers = (attachments, messenger) => {
|
|
14
|
-
const archive =
|
|
14
|
+
const archive = attachments.command("jobs").description("daily media archive timers for this profile");
|
|
15
15
|
for (const action of ["install", "start", "stop", "status", "uninstall"]) {
|
|
16
16
|
const command = annotate(archive.command(action), {
|
|
17
17
|
effects: action === "status" ? LOCAL_READ : LOCAL_WRITE,
|
|
18
|
-
mcp: false,
|
|
18
|
+
...(action === "status" ? {} : { mcp: false }),
|
|
19
19
|
examples: [`${messenger.app.command} attachments jobs ${action} ./media-archive`],
|
|
20
20
|
})
|
|
21
21
|
.description({
|
|
@@ -17,6 +17,7 @@ import { linksCommand } from "./conversations-command.js";
|
|
|
17
17
|
import { downloadSubcommand } from "./download-command.js";
|
|
18
18
|
import { heardItems, hearForCommand, hearingMissing, MODEL_OPTION, spokenItems, TRANSCRIBE_OPTION, } from "./hearing-command.js";
|
|
19
19
|
import { cursorOf, historyQuery, historyWindow } from "./history-query.js";
|
|
20
|
+
import { hideNever } from "./lacks.js";
|
|
20
21
|
import { threadAnswer } from "./message-answers.js";
|
|
21
22
|
import { messageItem, messageItems, viewOf } from "./message-items.js";
|
|
22
23
|
import { commentsCommand } from "./messages-comments-command.js";
|
|
@@ -174,7 +175,7 @@ export const messagesCommand = (messenger) => {
|
|
|
174
175
|
messages.addCommand(commentsCommand(messenger));
|
|
175
176
|
messages.addCommand(linksCommand(messenger));
|
|
176
177
|
messages.addCommand(syncCommand(messenger));
|
|
177
|
-
return messages;
|
|
178
|
+
return hideNever(messenger, messages);
|
|
178
179
|
};
|
|
179
180
|
/** Mail lives in the mail tables of the store, whichever messenger CLI reads it. */
|
|
180
181
|
const isMailLocator = (chat, message) => message === undefined && isLocator(chat) && parseLocator(chat).provider === "email";
|
|
@@ -12,7 +12,7 @@ export const messagesStatsCommand = (messenger) => syncOptions(annotate(new Comm
|
|
|
12
12
|
.summary("count stored messages by chat, sender, day or hour")
|
|
13
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" : ""}`)
|
|
14
14
|
.argument("[query...]", "a strict Lucene query, as for search messages; none counts every stored message; with --saved, more words AND-ed to it")
|
|
15
|
-
.option("--by <
|
|
15
|
+
.option("--by <group>", "count by chat, sender, day or hour (default: chat)", groupingOf)
|
|
16
16
|
.option("--chat <chat>", `only this chat — the same as chat: in the query; ${messenger.chatArgument}`)
|
|
17
17
|
.option("--source <accounts>", "every account of this messenger held in the store; personal, bots or all — the same as in: in the query")
|
|
18
18
|
.option("--limit <n>", "how many rows", positiveCount("--limit"))
|
|
@@ -23,7 +23,7 @@ export const metadataCommand = (messenger) => {
|
|
|
23
23
|
.description("fetch group and channel descriptions again and cache them")
|
|
24
24
|
.option("--chat <chat>", "stored group/channel; repeat for several", (value, previous) => [...previous, value], [])
|
|
25
25
|
.option("--only-missing", "only chats with no metadata yet; without --chat, every stored group/channel")
|
|
26
|
-
.option("--limit <
|
|
26
|
+
.option("--limit <n>", "process at most 1–500 chats", positiveCount("--limit"), 50)
|
|
27
27
|
.action(async function () {
|
|
28
28
|
const context = messengerContext(this, messenger);
|
|
29
29
|
refuseLocalWrite(context, messenger.app.command, "metadata.refresh");
|
|
@@ -58,7 +58,7 @@ const modelLine = (item, width) => `${item.default ? "*" : " "} ${item.id.padEnd
|
|
|
58
58
|
(item.downloaded ? "downloaded" : "—".padEnd(10));
|
|
59
59
|
export const modelsCommand = (messenger) => {
|
|
60
60
|
const tool = `${messenger.app.command} personal`;
|
|
61
|
-
const models =
|
|
61
|
+
const models = new Command("models").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
64
|
.summary("speech models and their download status")
|
|
@@ -76,6 +76,7 @@ export const modelsCommand = (messenger) => {
|
|
|
76
76
|
});
|
|
77
77
|
});
|
|
78
78
|
reachesOtherService(annotate(audio.command("download"), {
|
|
79
|
+
mcp: false,
|
|
79
80
|
effects: FETCH,
|
|
80
81
|
output: { entity: "model" },
|
|
81
82
|
examples: [`${tool} models audio download parakeet-v3`],
|
|
@@ -123,6 +124,7 @@ export const modelsCommand = (messenger) => {
|
|
|
123
124
|
});
|
|
124
125
|
});
|
|
125
126
|
reachesOtherService(annotate(text.command("download"), {
|
|
127
|
+
mcp: false,
|
|
126
128
|
effects: FETCH,
|
|
127
129
|
output: { entity: "model" },
|
|
128
130
|
examples: [`${tool} models text download ${DEFAULT_TEXT_MODEL}`],
|
|
@@ -171,7 +173,7 @@ export const modelsCommand = (messenger) => {
|
|
|
171
173
|
/** `images models`: registered with picture search, the only thing these models serve. */
|
|
172
174
|
export const imageModelsCommand = (messenger) => {
|
|
173
175
|
const tool = messenger.app.command;
|
|
174
|
-
const image =
|
|
176
|
+
const image = new Command("models").description("models that read pictures, for `images embed` and picture search");
|
|
175
177
|
withQuery(annotate(image.command("list"), { effects: LOCAL_READ, examples: [`${tool} images models list`] }), MODELS_QUERY)
|
|
176
178
|
.description("the picture models, which are downloaded, and which one is the default")
|
|
177
179
|
.action(async function (words) {
|
|
@@ -187,6 +189,7 @@ export const imageModelsCommand = (messenger) => {
|
|
|
187
189
|
});
|
|
188
190
|
});
|
|
189
191
|
reachesOtherService(annotate(image.command("download"), {
|
|
192
|
+
mcp: false,
|
|
190
193
|
effects: FETCH,
|
|
191
194
|
output: { entity: "model" },
|
|
192
195
|
examples: [`${tool} images models download ${DEFAULT_IMAGE_MODEL}`],
|
|
@@ -54,10 +54,11 @@ export const privatePeopleCommands = (messenger) => {
|
|
|
54
54
|
output: { entity: "contact_note" },
|
|
55
55
|
}), { name: "text", description: "the note's text" })
|
|
56
56
|
.description(name === "add"
|
|
57
|
-
? "add a private note on a person from stdin or --file; never sent"
|
|
57
|
+
? "add a private note on a person from stdin or --text-file; never sent"
|
|
58
58
|
: "replace a note's text; --revision must match the one you read")
|
|
59
59
|
.argument("<person>", "the person's id, reference or known name")
|
|
60
|
-
.addOption(ownerOnlyOption("--file <path>", "read note text from a file; omitted or - reads stdin"))
|
|
60
|
+
.addOption(ownerOnlyOption("--text-file <path>", "read note text from a file; omitted or - reads stdin").conflicts("file"))
|
|
61
|
+
.addOption(ownerOnlyOption("--file <path>", "compatibility alias for --text-file").hideHelp().conflicts("textFile"));
|
|
61
62
|
if (name === "edit")
|
|
62
63
|
command
|
|
63
64
|
.argument("<id>", "the note id from contacts notes list")
|
|
@@ -65,7 +66,8 @@ export const privatePeopleCommands = (messenger) => {
|
|
|
65
66
|
command.action(async function (person, id) {
|
|
66
67
|
const context = messengerContext(this, messenger);
|
|
67
68
|
refuseLocalWrite(context, messenger.app.command, `contacts.notes.${name}`);
|
|
68
|
-
const { file, revision } = this.opts();
|
|
69
|
+
const { textFile, file: legacyFile, revision, } = this.opts();
|
|
70
|
+
const file = textFile ?? legacyFile;
|
|
69
71
|
const text = file === undefined || file === "-" ? await readAll(context.stdin) : await readFile(file, "utf8");
|
|
70
72
|
const version = Number(revision);
|
|
71
73
|
if (name === "edit" && (!Number.isSafeInteger(version) || version < 1))
|
|
@@ -14,7 +14,7 @@ import { replyConsentsCommand } from "./replies-consents-command.js";
|
|
|
14
14
|
import { addReplyEditors } from "./replies-edit-command.js";
|
|
15
15
|
/** `replies`: rules that answer messages for the owner, from `serve`, and only to the people and chats the audience allows. */
|
|
16
16
|
export const repliesCommand = (messenger) => {
|
|
17
|
-
const replies =
|
|
17
|
+
const replies = new Command("replies").description("rules that answer messages for you, kept in a file of this profile");
|
|
18
18
|
if (messenger.autoReplies === false)
|
|
19
19
|
replies.hook("preAction", () => {
|
|
20
20
|
throw new CliError("validation_error", `${messenger.name ?? messenger.app.command} cannot send automatic replies`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { type Messenger } from "./context.js";
|
|
3
|
-
export declare const REPLY_EDIT_OPTIONS: readonly [readonly ["--do <actions>", "actions: reply, task, or both, comma-separated"], readonly ["--kinds <kinds>", "chat kinds: dialog, group; comma-separated, empty for any"], readonly ["--allow-chats <ids>", "only these chat ids, comma-separated; empty for any"], readonly ["--deny-chats <ids>", "leave these chat ids out, comma-separated; empty clears"], readonly ["--words <words>", "match any of these whole words, comma-separated; empty clears"], readonly ["--question", "match only questions"], readonly ["--no-question", "do not require a question"], readonly ["--mentions-me", "require a mention of you or a reply to you"], readonly ["--no-mentions-me", "do not require a mention of you or a reply to you"], readonly ["--allow-people <ids>", "only these sender ids, comma-separated; empty for any"], readonly ["--deny-people <ids>", "leave these sender ids out, comma-separated; empty clears"], readonly ["--contacts-only", "match only contacts"], readonly ["--no-contacts-only", "do not require a contact"], readonly ["--template <text>", "the reply template"], readonly ["--
|
|
3
|
+
export declare const REPLY_EDIT_OPTIONS: readonly [readonly ["--do <actions>", "actions: reply, task, or both, comma-separated"], readonly ["--kinds <kinds>", "chat kinds: dialog, group; comma-separated, empty for any"], readonly ["--allow-chats <ids>", "only these chat ids, comma-separated; empty for any"], readonly ["--deny-chats <ids>", "leave these chat ids out, comma-separated; empty clears"], readonly ["--words <words>", "match any of these whole words, comma-separated; empty clears"], readonly ["--question", "match only questions"], readonly ["--no-question", "do not require a question"], readonly ["--mentions-me", "require a mention of you or a reply to you"], readonly ["--no-mentions-me", "do not require a mention of you or a reply to you"], readonly ["--allow-people <ids>", "only these sender ids, comma-separated; empty for any"], readonly ["--deny-people <ids>", "leave these sender ids out, comma-separated; empty clears"], readonly ["--contacts-only", "match only contacts"], readonly ["--no-contacts-only", "do not require a contact"], readonly ["--template <text>", "the reply template"], readonly ["--template-mode <mode>", "legacy template mode: fill-only or may-reword; use ai blocks instead"], readonly ["--as-reply", "send as a reply to the matched message"], readonly ["--no-as-reply", "send without linking to the matched message"], readonly ["--per-chat <limit>", "at most this many per chat, such as 1/12h"], readonly ["--per-person <limit>", "at most this many per person, such as 1/1d"], readonly ["--outside <hours>", "answer outside this 24-hour window, such as 09:00-19:00"], readonly ["--days <days>", "days of the working window, such as mon-fri or sat,sun"], readonly ["--timezone <zone>", "the IANA timezone for the working window"], readonly ["--no-hours", "clear the working window"]];
|
|
4
4
|
export declare const REPLY_AUDIENCE_OPTIONS: readonly [readonly ["--reply <mode>", "answer all or only listed senders and chats: all, listed"], readonly ["--allow-people <ids>", "replace allowed sender ids, comma-separated; empty clears"], readonly ["--allow-chats <ids>", "replace allowed chat ids, comma-separated; empty clears"], readonly ["--deny-people <ids>", "replace denied sender ids, comma-separated; empty clears; deny wins"], readonly ["--deny-chats <ids>", "replace denied chat ids, comma-separated; empty clears; deny wins"]];
|
|
5
5
|
export declare const addReplyEditors: (replies: Command, messenger: Messenger) => void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { annotate } from "@wirecat/cli-base/commands";
|
|
2
|
-
import { Command } from "commander";
|
|
2
|
+
import { Command, Option } from "commander";
|
|
3
3
|
import { addRule, editAudience, editRule, switchRule } from "../../replies/edit.js";
|
|
4
4
|
import { audienceWarnings, repliesPathFor } from "../../replies/rules.js";
|
|
5
5
|
import { grantsAuthority, LOCAL_WRITE } from "../effects.js";
|
|
@@ -20,7 +20,7 @@ export const REPLY_EDIT_OPTIONS = [
|
|
|
20
20
|
["--contacts-only", "match only contacts"],
|
|
21
21
|
["--no-contacts-only", "do not require a contact"],
|
|
22
22
|
["--template <text>", "the reply template"],
|
|
23
|
-
["--
|
|
23
|
+
["--template-mode <mode>", "legacy template mode: fill-only or may-reword; use ai blocks instead"],
|
|
24
24
|
["--as-reply", "send as a reply to the matched message"],
|
|
25
25
|
["--no-as-reply", "send without linking to the matched message"],
|
|
26
26
|
["--per-chat <limit>", "at most this many per chat, such as 1/12h"],
|
|
@@ -59,8 +59,13 @@ export const addReplyEditors = (replies, messenger) => {
|
|
|
59
59
|
const edit = grantsAuthority(annotate(new Command("edit"), { effects: LOCAL_WRITE }))
|
|
60
60
|
.description("change only the named fields of a reply rule; lists replace the whole list")
|
|
61
61
|
.argument("<id>", "the rule's id");
|
|
62
|
-
for (const [flag, help] of REPLY_EDIT_OPTIONS)
|
|
63
|
-
|
|
62
|
+
for (const [flag, help] of REPLY_EDIT_OPTIONS) {
|
|
63
|
+
const option = new Option(flag, help);
|
|
64
|
+
if (flag === "--template-mode <mode>")
|
|
65
|
+
option.conflicts("model");
|
|
66
|
+
edit.addOption(option);
|
|
67
|
+
}
|
|
68
|
+
edit.addOption(new Option("--model <mode>", "compatibility alias for --template-mode").hideHelp().conflicts("templateMode"));
|
|
64
69
|
// Commander gives negated-only flags a true default; omission must preserve the saved field.
|
|
65
70
|
edit.action(function (id) {
|
|
66
71
|
const options = this.opts();
|
|
@@ -68,7 +73,10 @@ export const addReplyEditors = (replies, messenger) => {
|
|
|
68
73
|
delete options.hours;
|
|
69
74
|
const context = messengerContext(this, messenger);
|
|
70
75
|
const { settings, env } = context;
|
|
71
|
-
renderItem(this, context, editRule(repliesPathFor(messenger.app, settings.profile, env), messenger.provider, id,
|
|
76
|
+
renderItem(this, context, editRule(repliesPathFor(messenger.app, settings.profile, env), messenger.provider, id, {
|
|
77
|
+
...options,
|
|
78
|
+
model: options.templateMode ?? options.model,
|
|
79
|
+
}));
|
|
72
80
|
});
|
|
73
81
|
replies.addCommand(edit);
|
|
74
82
|
const audience = grantsAuthority(annotate(new Command("audience"), { effects: LOCAL_WRITE })).description("show the reply audience, who the rules may answer, or replace its named fields; a new file answers everyone a rule matches");
|
|
@@ -13,7 +13,7 @@ export const retentionCommand = (messenger) => annotate(new Command("retention")
|
|
|
13
13
|
.option("--until-time <time>", "joining period ends at this time; now by default")
|
|
14
14
|
.option("--checkpoints <durations>", "up to 10 increasing joining ages, comma separated; 1d,7d,30d by default")
|
|
15
15
|
.option("--within <duration>", "activity and early departure window after joining; 7d by default")
|
|
16
|
-
.addOption(new Option("--by <
|
|
16
|
+
.addOption(new Option("--by <group>", "group joining dates by calendar day or Monday week").choices(["day", "week"]))
|
|
17
17
|
.option("--timezone <zone>", "IANA timezone for joining cohorts")
|
|
18
18
|
.option("--limit <n>", "cohorts and member evidence, 1–100", positiveCount("--limit"))
|
|
19
19
|
.action(async function (chat) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CliError } from "@wirecat/cli-base";
|
|
2
2
|
import { annotate } from "@wirecat/cli-base/commands";
|
|
3
|
-
import { Command } from "commander";
|
|
3
|
+
import { Command, Option } from "commander";
|
|
4
4
|
import { parseDuration } from "../../domain/duration.js";
|
|
5
5
|
import { renderMessages } from "../../render/messages.js";
|
|
6
6
|
import { CHAT_KINDS } from "../../services/chats.js";
|
|
@@ -10,7 +10,7 @@ import { REMOTE_READ } from "../effects.js";
|
|
|
10
10
|
import { renderAnswer } from "../envelope.js";
|
|
11
11
|
import { queryHelp } from "../list-query.js";
|
|
12
12
|
import { pointsOf } from "./catch-up.js";
|
|
13
|
-
import { catchUpQuery, REVIEW_QUERY, reviewAnswer } from "./catch-up-answers.js";
|
|
13
|
+
import { catchUpQuery, OFFLINE_REVIEW, REVIEW_QUERY, reviewAnswer } from "./catch-up-answers.js";
|
|
14
14
|
import { messengerContext } from "./context.js";
|
|
15
15
|
import { heardItems, hearForCommand, MODEL_OPTION, spokenItems, TRANSCRIBE_OPTION } from "./hearing-command.js";
|
|
16
16
|
import { PARTIAL_NOTE } from "./inbox.js";
|
|
@@ -25,6 +25,7 @@ export const unansweredHours = (value, flag = "--unanswered") => value === true
|
|
|
25
25
|
* tasks in the local store from what it read (`../../services/task-rules.ts`); nothing reaches the messenger.
|
|
26
26
|
*/
|
|
27
27
|
export const reviewCommand = (messenger) => annotate(new Command("review"), {
|
|
28
|
+
examples: [`${messenger.app.command} personal review "date>2h"`],
|
|
28
29
|
effects: REMOTE_READ,
|
|
29
30
|
output: { entity: "message", view: "summary" },
|
|
30
31
|
query: REVIEW_QUERY,
|
|
@@ -33,19 +34,22 @@ export const reviewCommand = (messenger) => annotate(new Command("review"), {
|
|
|
33
34
|
.description("every message, yours too, in chats that changed since a point — for reviewing who owes what")
|
|
34
35
|
.addHelpText("after", queryHelp(REVIEW_QUERY))
|
|
35
36
|
.argument("[query...]", `where the last review ended and which chat: date>2h, chat:"Book club"; ${REVIEW_DAYS} days ago if no date`)
|
|
36
|
-
.
|
|
37
|
+
.addOption(new Option("--kind <kinds>", `compatibility spelling for kind:; ${CHAT_KINDS.join(", ")}`).hideHelp())
|
|
37
38
|
.option("--unanswered [duration]", `only questions to you or a group's admins that nobody answered, asked at least this long ago — 4h, 1d; ${UNANSWERED_HOURS}h if not given`)
|
|
38
39
|
.option("--all", "muted and archived chats too — left out unless they mention you or reply to you")
|
|
39
40
|
.option(...TRANSCRIBE_OPTION)
|
|
40
41
|
.option(...MODEL_OPTION)
|
|
41
42
|
.option("--new", "what changed since the last `review --new`, a point per chat — for scheduled runs")
|
|
43
|
+
.addHelpText("after", "With --offline it reads the local store: only what was already synced, and --new keeps its point.")
|
|
42
44
|
.addHelpText("after", "To mark a chat read, use `chats mark-read <chat>`. The other side sees it.")
|
|
43
45
|
.action(async function (query) {
|
|
44
46
|
const options = this.opts();
|
|
45
47
|
const hearWith = modelWith(options.transcribe, options.model);
|
|
46
48
|
const context = messengerContext(this, messenger);
|
|
47
49
|
const { settings, renderer, format, env } = context;
|
|
48
|
-
const { since: from, chat, meta } = catchUpQuery(query, REVIEW_QUERY, "review");
|
|
50
|
+
const { since: from, chat, meta, kinds: queriedKinds } = catchUpQuery(query, REVIEW_QUERY, "review");
|
|
51
|
+
if (options.kind !== undefined && queriedKinds !== undefined)
|
|
52
|
+
throw new CliError("validation_error", "give kind: or its legacy --kind spelling, not both");
|
|
49
53
|
if (options.new && (from !== undefined || options.unanswered !== undefined)) {
|
|
50
54
|
// An open question stays open: moving past it would drop it from the next review.
|
|
51
55
|
throw new CliError("validation_error", "--new keeps its own point — not with a date or --unanswered");
|
|
@@ -60,7 +64,7 @@ export const reviewCommand = (messenger) => annotate(new Command("review"), {
|
|
|
60
64
|
: undefined;
|
|
61
65
|
const since = from ?? saved?.first ?? reviewStart();
|
|
62
66
|
const hours = options.unanswered === undefined ? undefined : unansweredHours(options.unanswered);
|
|
63
|
-
const kinds = options.kind === undefined ? undefined : kindsOf(options.kind);
|
|
67
|
+
const kinds = queriedKinds ?? (options.kind === undefined ? undefined : kindsOf(options.kind));
|
|
64
68
|
let hearing;
|
|
65
69
|
const transcribe = options.transcribe === true;
|
|
66
70
|
const { found, reader } = await context.withServices(async (services, connect) => {
|
|
@@ -99,7 +103,8 @@ export const reviewCommand = (messenger) => annotate(new Command("review"), {
|
|
|
99
103
|
.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
100
104
|
return read.length === 0 ? "" : renderMessages(spokenItems(read, hearing), viewOf(context, messenger));
|
|
101
105
|
});
|
|
102
|
-
|
|
106
|
+
if (!found.offline)
|
|
107
|
+
saved?.save(found.checked ?? {});
|
|
103
108
|
});
|
|
104
109
|
/** What the answer's steps do not say: a person's context for the review just shown. */
|
|
105
110
|
const notes = (found, pretty, renderer) => {
|
|
@@ -111,6 +116,8 @@ const notes = (found, pretty, renderer) => {
|
|
|
111
116
|
(found.tasks.review > 0 ? `, ${found.tasks.review} pair(s) to review — tasks duplicates` : ""));
|
|
112
117
|
if (found.partial && pretty)
|
|
113
118
|
renderer.warn(PARTIAL_NOTE);
|
|
119
|
+
if (found.offline && pretty)
|
|
120
|
+
renderer.warn(OFFLINE_REVIEW);
|
|
114
121
|
if (found.unanswered)
|
|
115
122
|
renderer.note(`questions from ${found.since} to ${found.until} still open after ${found.unanswered.olderThanHours} hours; ` +
|
|
116
123
|
"an answer after the review's end is not seen");
|
|
@@ -61,7 +61,7 @@ const withWhen = (command) => command
|
|
|
61
61
|
*/
|
|
62
62
|
export const scheduleCommand = (messenger) => {
|
|
63
63
|
const { app } = messenger;
|
|
64
|
-
const command =
|
|
64
|
+
const command = new Command("schedule").description(`run a ${app.command} command or an agent later or on a timer (systemd, launchd, Task Scheduler); an agent cannot send`);
|
|
65
65
|
const prepare = (self) => {
|
|
66
66
|
const context = messengerContext(self, messenger);
|
|
67
67
|
const system = environmentOf(self).system ?? thisMachine();
|
|
@@ -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, mcp: false })
|
|
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, mcp: false })
|
|
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) {
|
|
@@ -36,8 +36,8 @@ const allCommand = (messenger) => {
|
|
|
36
36
|
const command = annotate(new Command("all"), { effects: REMOTE_READ, partial: true })
|
|
37
37
|
.summary("search stored messages, notes and selected meeting transcripts")
|
|
38
38
|
.description((messenger.serverSearch
|
|
39
|
-
? "search messenger messages and notes in the local store, and the messenger's server for
|
|
40
|
-
"(--backend
|
|
39
|
+
? "search messenger messages and notes in the local store, and by default the messenger's server for " +
|
|
40
|
+
"messages too (notes are local only; --backend archive or --offline keeps it to the local store)"
|
|
41
41
|
: "search messenger messages and notes in the local store") +
|
|
42
42
|
", and a meeting account's transcripts when --source names one — best match first; not mail (the separate memo CLI searches it: memo search mail), " +
|
|
43
43
|
"tasks, people, memories or projects. Use it when you do not know where something was written; search messages " +
|
|
@@ -45,7 +45,7 @@ export const searchesCommand = (messenger) => {
|
|
|
45
45
|
.option("--exact", "bare words and quotes match their exact form only, as exact:word does; text: still matches every form")
|
|
46
46
|
.option("--context <n>", "messages before and after each hit", wholeCount)
|
|
47
47
|
.option("--timezone <zone>", "the IANA timezone for calendar date boundaries")
|
|
48
|
-
.option("--by <
|
|
48
|
+
.option("--by <group>", "count by chat, sender, day or hour for stats messages show --saved", groupingOf)
|
|
49
49
|
.option("--selection <json>", "save the resolved parent ranking query and options from a drilldown")
|
|
50
50
|
.option("--replace", "overwrite a saved search of the same name")
|
|
51
51
|
.action(async function (name, words) {
|
|
@@ -30,7 +30,11 @@ export { lockPath, readLock, servingProfiles } from "../../background/lock.js";
|
|
|
30
30
|
* taken over. Started by a person, a service unit or `server start` — never by itself (NEED-9).
|
|
31
31
|
*/
|
|
32
32
|
export const serveCommand = (messenger) => {
|
|
33
|
-
const command = annotate(new Command("serve"), {
|
|
33
|
+
const command = annotate(new Command("serve"), {
|
|
34
|
+
effects: REMOTE_WRITE,
|
|
35
|
+
lifetime: "persistent",
|
|
36
|
+
examples: [`${messenger.app.command} personal serve`],
|
|
37
|
+
}).description("keep the local store current until stopped — what a systemd or launchd unit runs");
|
|
34
38
|
command.action(async function () {
|
|
35
39
|
const relay = messenger.connection === "exclusive" && process.platform !== "win32" ? serveRelay(this, messenger) : undefined;
|
|
36
40
|
const context = recordDiagnostics(messengerContext(this, relay?.messenger ?? messenger), messenger.app);
|