@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
package/dist/cli/bot/setup.js
CHANGED
|
@@ -7,6 +7,7 @@ import { REMOTE_READ, REMOTE_WRITE } from "../effects.js";
|
|
|
7
7
|
import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
|
|
8
8
|
import { wholeList } from "../messenger/chat-answers.js";
|
|
9
9
|
import { botContext, online } from "./context.js";
|
|
10
|
+
import { botExamples } from "./examples.js";
|
|
10
11
|
import { botCan } from "./messages.js";
|
|
11
12
|
import { botFiles } from "./registry.js";
|
|
12
13
|
import { PressLog } from "./updates.js";
|
|
@@ -30,7 +31,7 @@ const botWrite = (context, key, write) => guardedWrite(context.guard(), { operat
|
|
|
30
31
|
/** `bot commands`: the menu people see when they type `/`. */
|
|
31
32
|
export const botMenuCommand = (bot) => {
|
|
32
33
|
const command = new Command("commands").description("the bot's command menu — what people see after /");
|
|
33
|
-
annotate(command.command("list"), { effects: REMOTE_READ })
|
|
34
|
+
annotate(command.command("list"), { effects: REMOTE_READ, examples: botExamples(bot, "commands list") })
|
|
34
35
|
.description("the commands in the menu now")
|
|
35
36
|
.action(async function () {
|
|
36
37
|
const context = online(botContext(this, bot), this);
|
|
@@ -48,13 +49,19 @@ export const botMenuCommand = (bot) => {
|
|
|
48
49
|
renderAnswer(command, context, wholeList("bot commands list", entries));
|
|
49
50
|
});
|
|
50
51
|
};
|
|
51
|
-
annotate(command.command("set"), {
|
|
52
|
+
annotate(command.command("set"), {
|
|
53
|
+
effects: REMOTE_WRITE,
|
|
54
|
+
examples: botExamples(bot, 'commands set "start=Begin here" "help=What I can do"'),
|
|
55
|
+
})
|
|
52
56
|
.description("replace the whole menu: each command as name=description, e.g. start=Begin")
|
|
53
57
|
.argument("<commands...>", "name=description, one per command")
|
|
54
58
|
.action(async function (typed) {
|
|
55
59
|
await replace(this, typed.map(entryOf), "bot.commands.set");
|
|
56
60
|
});
|
|
57
|
-
annotate(command.command("clear"), {
|
|
61
|
+
annotate(command.command("clear"), {
|
|
62
|
+
effects: REMOTE_WRITE,
|
|
63
|
+
examples: botExamples(bot, "commands clear"),
|
|
64
|
+
})
|
|
58
65
|
.description("empty the menu")
|
|
59
66
|
.action(async function () {
|
|
60
67
|
await replace(this, [], "bot.commands.clear");
|
|
@@ -62,9 +69,13 @@ export const botMenuCommand = (bot) => {
|
|
|
62
69
|
return command;
|
|
63
70
|
};
|
|
64
71
|
/** `bot callbacks`: answers to the buttons people press under the bot's messages. */
|
|
65
|
-
export const botCallbacksCommand = (bot) => new Command("callbacks").description("answers to the buttons people press under the bot's messages").addCommand(annotate(new Command("answer"), {
|
|
72
|
+
export const botCallbacksCommand = (bot) => new Command("callbacks").description("answers to the buttons people press under the bot's messages").addCommand(annotate(new Command("answer"), {
|
|
73
|
+
effects: REMOTE_WRITE,
|
|
74
|
+
examples: botExamples(bot, 'callbacks answer 4385 --notification "Saved"'),
|
|
75
|
+
})
|
|
66
76
|
.description("answer a pressed button by its callback id: --notification shows the person a one-time note, " +
|
|
67
77
|
"--text replaces the message the button was on")
|
|
78
|
+
.summary("answer a pressed button: a note to that person, or new message text")
|
|
68
79
|
.argument("<callback>", "the callback id `bot watch` printed")
|
|
69
80
|
.option("--text <text>", "the message's new text")
|
|
70
81
|
.option("--notification <text>", "a note only the person who pressed sees")
|
|
@@ -97,8 +108,10 @@ export const botCallbacksCommand = (bot) => new Command("callbacks").description
|
|
|
97
108
|
}));
|
|
98
109
|
/** `bot webhooks`: where the messenger pushes this bot's updates instead of being polled. */
|
|
99
110
|
export const botWebhooksCommand = (bot) => {
|
|
100
|
-
const command =
|
|
101
|
-
|
|
111
|
+
const command = new Command("webhooks")
|
|
112
|
+
.description("where the messenger pushes this bot's updates — while one is set, `bot watch` gets nothing")
|
|
113
|
+
.summary("where the messenger pushes this bot's updates");
|
|
114
|
+
annotate(command.command("list"), { effects: REMOTE_READ, examples: botExamples(bot, "webhooks list") })
|
|
102
115
|
.description("the webhooks this bot has")
|
|
103
116
|
.action(async function () {
|
|
104
117
|
const context = online(botContext(this, bot), this);
|
|
@@ -107,7 +120,10 @@ export const botWebhooksCommand = (bot) => {
|
|
|
107
120
|
renderAnswer(this, context, wholeList("bot webhooks list", (await botCan(adapter, "webhooks", bot, "list its webhooks")()).map(snakeKeys)));
|
|
108
121
|
});
|
|
109
122
|
});
|
|
110
|
-
const set = annotate(command.command("set"), {
|
|
123
|
+
const set = annotate(command.command("set"), {
|
|
124
|
+
effects: REMOTE_WRITE,
|
|
125
|
+
examples: botExamples(bot, "webhooks set https://bot.example.ts.net/"),
|
|
126
|
+
})
|
|
111
127
|
.description("send this bot's updates to an HTTPS address; refused while another is set")
|
|
112
128
|
.argument("<url>", "the HTTPS address")
|
|
113
129
|
.option("--types <types>", "only these update types, comma-separated, in the messenger's words")
|
|
@@ -138,7 +154,10 @@ export const botWebhooksCommand = (bot) => {
|
|
|
138
154
|
renderAnswer(this, context, wholeList("bot webhooks list", (await list()).map(snakeKeys)));
|
|
139
155
|
});
|
|
140
156
|
});
|
|
141
|
-
annotate(command.command("delete"), {
|
|
157
|
+
annotate(command.command("delete"), {
|
|
158
|
+
effects: REMOTE_WRITE,
|
|
159
|
+
examples: botExamples(bot, "webhooks delete https://bot.example.ts.net/"),
|
|
160
|
+
})
|
|
142
161
|
.description("stop sending updates to this address; with none left, `bot watch` works again")
|
|
143
162
|
.argument("<url>", "the address")
|
|
144
163
|
.action(async function (url) {
|
package/dist/cli/bot/watch.js
CHANGED
|
@@ -7,6 +7,7 @@ import { environmentOf } from "../context.js";
|
|
|
7
7
|
import { REMOTE_WRITE } from "../effects.js";
|
|
8
8
|
import { withoutProviderRefs } from "../provider-refs.js";
|
|
9
9
|
import { botContext, online } from "./context.js";
|
|
10
|
+
import { botExamples } from "./examples.js";
|
|
10
11
|
import { botCan, botIdOf } from "./messages.js";
|
|
11
12
|
import { botFiles } from "./registry.js";
|
|
12
13
|
import { PressLog, UpdatesCursor } from "./updates.js";
|
|
@@ -83,8 +84,13 @@ const keep = async (command, context, bot, botId, events, senders) => {
|
|
|
83
84
|
* With `--public-url` the messenger pushes each batch to a local receiver behind the owner's tunnel
|
|
84
85
|
* instead, and a batch is acknowledged only once kept, so the messenger sends again what was not.
|
|
85
86
|
*/
|
|
86
|
-
export const botWatchCommand = (bot) => annotate(new Command("watch"), {
|
|
87
|
+
export const botWatchCommand = (bot) => annotate(new Command("watch"), {
|
|
88
|
+
effects: REMOTE_WRITE,
|
|
89
|
+
lifetime: "persistent",
|
|
90
|
+
examples: botExamples(bot, "watch --jsonl", "watch --events"),
|
|
91
|
+
})
|
|
87
92
|
.description("print new messages as they arrive and keep them, until Ctrl-C or --timeout (either ends it normally)")
|
|
93
|
+
.summary("print new messages as they arrive and keep them, until Ctrl-C")
|
|
88
94
|
.option("--events", "also edits, deletions, buttons pressed and people coming and going; every line names its event")
|
|
89
95
|
.option("--types <types>", "only these update types, comma-separated, in the messenger's words")
|
|
90
96
|
.option("--public-url <url>", "receive by webhook at your tunnel's https address instead of polling, e.g. https://<name>.ts.net")
|
|
@@ -50,7 +50,7 @@ export const commandsCommand = (app) => {
|
|
|
50
50
|
exit_codes: { ok: 0, [CRASH_CODE]: GENERIC_FAILURE, ...EXIT_CODES },
|
|
51
51
|
});
|
|
52
52
|
});
|
|
53
|
-
command.addCommand(annotate(new Command("schema"), { effects: NO_EFFECTS
|
|
53
|
+
command.addCommand(annotate(new Command("schema"), { effects: NO_EFFECTS })
|
|
54
54
|
.description("one command's argv and result schemas, effects, permissions and retry guidance")
|
|
55
55
|
.argument("<path...>", "one command path, for example: stats messages show")
|
|
56
56
|
.action(function (path) {
|
|
@@ -15,7 +15,7 @@ import { changeStoreSetting, isStoreSetting, STORE_SETTINGS, storeSettings } fro
|
|
|
15
15
|
* configuration accepts can hold a secret — the schema has nowhere to put one.
|
|
16
16
|
*/
|
|
17
17
|
export const configCommand = (app, config, { permissionKey } = {}) => {
|
|
18
|
-
const command =
|
|
18
|
+
const command = new Command("config").description("the settings in force, and where each one came from");
|
|
19
19
|
annotate(command.command("show"), { effects: LOCAL_READ, examples: [`${app.command} personal config show`] })
|
|
20
20
|
.description("the profile, the profiles that exist, and each setting with where it came from")
|
|
21
21
|
.option("--bot", "the settings a bot command on this profile gets, rather than the personal account's")
|
|
@@ -3,6 +3,7 @@ import { type AISettings } from "../analysis/settings.js";
|
|
|
3
3
|
import { type AppIdentity } from "../ports/app.js";
|
|
4
4
|
import type { ModelChoice } from "../services/embeddings.js";
|
|
5
5
|
export interface ModelOptions {
|
|
6
|
+
embeddingProvider?: string;
|
|
6
7
|
model?: string;
|
|
7
8
|
provider?: string;
|
|
8
9
|
baseUrl?: string;
|
|
@@ -10,7 +11,7 @@ export interface ModelOptions {
|
|
|
10
11
|
concurrency?: number;
|
|
11
12
|
}
|
|
12
13
|
/** A local model id, or a remote model with its key from `models text key set`. */
|
|
13
|
-
export declare const embeddingChoice: ({ model, provider, baseUrl, dims, concurrency }: ModelOptions, app: AppIdentity, settings: AISettings, env: NodeJS.ProcessEnv, { needKey, keyring }?: {
|
|
14
|
+
export declare const embeddingChoice: ({ model, provider, embeddingProvider, baseUrl, dims, concurrency }: ModelOptions, app: AppIdentity, settings: AISettings, env: NodeJS.ProcessEnv, { needKey, keyring }?: {
|
|
14
15
|
needKey?: boolean;
|
|
15
16
|
keyring?: KeyringStore | undefined;
|
|
16
17
|
}) => ModelChoice;
|
|
@@ -6,7 +6,10 @@ import { isLocal, remoteModel } from "../embeddings/remote.js";
|
|
|
6
6
|
import { readOnlyLockOf, readOnlyRefusal } from "../ports/app.js";
|
|
7
7
|
import { typedCommand } from "../runs/scope.js";
|
|
8
8
|
/** A local model id, or a remote model with its key from `models text key set`. */
|
|
9
|
-
export const embeddingChoice = ({ model, provider, baseUrl, dims, concurrency }, app, settings, env, { needKey = true, keyring } = {}) => {
|
|
9
|
+
export const embeddingChoice = ({ model, provider, embeddingProvider, baseUrl, dims, concurrency }, app, settings, env, { needKey = true, keyring } = {}) => {
|
|
10
|
+
if (embeddingProvider !== undefined && provider !== undefined)
|
|
11
|
+
throw new CliError("validation_error", "give --embedding-provider or its legacy --provider spelling, not both");
|
|
12
|
+
provider = embeddingProvider ?? provider;
|
|
10
13
|
const flagsChooseProvider = (provider !== undefined && provider !== (settings.embeddingProvider ?? "local")) ||
|
|
11
14
|
(baseUrl !== undefined && baseUrl.replace(/\/+$/, "") !== settings.embeddingBaseUrl?.replace(/\/+$/, ""));
|
|
12
15
|
provider ??= flagsChooseProvider ? "openai" : (settings.embeddingProvider ?? "local");
|
package/dist/cli/guide.js
CHANGED
|
@@ -116,7 +116,6 @@ const scenarioText = (cli, scenario, profile) => [
|
|
|
116
116
|
export const guideCommand = (app, options) => {
|
|
117
117
|
const command = annotate(new Command("guide"), {
|
|
118
118
|
effects: NO_EFFECTS,
|
|
119
|
-
mcp: false,
|
|
120
119
|
examples: [`${app.command} guide`, `${app.command} guide <name> --json`],
|
|
121
120
|
})
|
|
122
121
|
.description("scenarios: the commands for a common task, in order, and why each")
|
|
@@ -7,6 +7,8 @@ export interface NextChoice {
|
|
|
7
7
|
}
|
|
8
8
|
/** Overrides the "Next usually" a command's help derives from types. Kept off `annotate`, like its section. */
|
|
9
9
|
export declare const nextUsually: <T extends Command>(command: T, choice: NextChoice) => T;
|
|
10
|
+
/** The profile an example names, so a pasted one runs; the name the first screen's "Start here" uses. */
|
|
11
|
+
export declare const EXAMPLE_PROFILE = "personal";
|
|
10
12
|
export interface CommandHelp {
|
|
11
13
|
returns: string;
|
|
12
14
|
next: readonly string[];
|
|
@@ -20,8 +22,13 @@ export declare const commandHelp: (command: Command) => CommandHelp;
|
|
|
20
22
|
export declare const commandHelpText: (command: Command) => string;
|
|
21
23
|
/** The same, as Markdown lines for `docs/commands.md`: hand it to cli-base's `commandsPage` per command. */
|
|
22
24
|
export declare const commandHelpMarkdown: (root: Command, path: readonly string[]) => string[];
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Puts Returns, Next usually and Example on every leaf command's `--help`, worked out when the help is shown.
|
|
27
|
+
* With `profile`, an example of an account command gets the profile word it would fail without.
|
|
28
|
+
*/
|
|
29
|
+
export declare const installCommandHelp: (program: Command, { profile }?: {
|
|
30
|
+
profile?: boolean;
|
|
31
|
+
}) => void;
|
|
25
32
|
/** Which leaf commands still show a generated example, so the important ones get a written one. Report only. */
|
|
26
33
|
export declare const exampleReport: (program: Command) => {
|
|
27
34
|
written: string[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { metaOf } from "@wirecat/cli-base/commands";
|
|
2
2
|
import { writes } from "@wirecat/cli-base/standards";
|
|
3
3
|
import { commandPathOf, outputOf } from "../command-contract.js";
|
|
4
|
+
import { PROFILE_FREE } from "../profile.js";
|
|
4
5
|
import { commandAt } from "../steps.js";
|
|
5
6
|
import { short } from "./commands-text.js";
|
|
6
7
|
/** Argument names onto the kinds `domain/references.ts` names; a plural takes the same kind. */
|
|
@@ -164,16 +165,28 @@ const generatedExample = (command) => {
|
|
|
164
165
|
return [flag];
|
|
165
166
|
return [flag, option.argChoices?.[0] ?? sample(placeholder ?? option.attributeName())];
|
|
166
167
|
});
|
|
167
|
-
// No profile word, as on the first screen and in written examples: the Usage line already shows `<profile>`.
|
|
168
168
|
return [rootOf(command).name(), ...commandPathOf(command), ...words, ...options].join(" ");
|
|
169
169
|
};
|
|
170
|
+
const profiled = new WeakSet();
|
|
171
|
+
/** The profile an example names, so a pasted one runs; the name the first screen's "Start here" uses. */
|
|
172
|
+
export const EXAMPLE_PROFILE = "personal";
|
|
173
|
+
const withProfile = (command, example) => {
|
|
174
|
+
const root = rootOf(command);
|
|
175
|
+
const top = commandPathOf(command)[0];
|
|
176
|
+
if (!profiled.has(root) || top === undefined || PROFILE_FREE.has(top))
|
|
177
|
+
return example;
|
|
178
|
+
const prefix = `${root.name()} ${top}`;
|
|
179
|
+
return example === prefix || example.startsWith(`${prefix} `)
|
|
180
|
+
? `${root.name()} ${EXAMPLE_PROFILE} ${example.slice(root.name().length + 1)}`
|
|
181
|
+
: example;
|
|
182
|
+
};
|
|
170
183
|
/** Returns, Next usually and Example for one command, from its definition alone. */
|
|
171
184
|
export const commandHelp = (command) => {
|
|
172
185
|
const written = metaOf(command).examples ?? [];
|
|
173
186
|
return {
|
|
174
187
|
returns: returnsOf(command),
|
|
175
188
|
next: nextOf(command).map(call),
|
|
176
|
-
examples: written.length > 0 ? written : [generatedExample(command)],
|
|
189
|
+
examples: (written.length > 0 ? written : [generatedExample(command)]).map((one) => withProfile(command, one)),
|
|
177
190
|
origin: written.length > 0 ? "written" : "generated",
|
|
178
191
|
};
|
|
179
192
|
};
|
|
@@ -197,8 +210,13 @@ export const commandHelpMarkdown = (root, path) => {
|
|
|
197
210
|
`**Example:** ${code(examples)}`,
|
|
198
211
|
];
|
|
199
212
|
};
|
|
200
|
-
/**
|
|
201
|
-
|
|
213
|
+
/**
|
|
214
|
+
* Puts Returns, Next usually and Example on every leaf command's `--help`, worked out when the help is shown.
|
|
215
|
+
* With `profile`, an example of an account command gets the profile word it would fail without.
|
|
216
|
+
*/
|
|
217
|
+
export const installCommandHelp = (program, { profile = true } = {}) => {
|
|
218
|
+
if (profile)
|
|
219
|
+
profiled.add(program);
|
|
202
220
|
for (const command of leaves(program))
|
|
203
221
|
command.addHelpText("after", () => commandHelpText(command));
|
|
204
222
|
};
|
|
@@ -4,7 +4,7 @@ import { Command, Help, Option } from "commander";
|
|
|
4
4
|
import wrapAnsi from "wrap-ansi";
|
|
5
5
|
import { NO_EFFECTS } from "../effects.js";
|
|
6
6
|
import { commandAt } from "../steps.js";
|
|
7
|
-
import { installCommandHelp } from "./command-help.js";
|
|
7
|
+
import { EXAMPLE_PROFILE, installCommandHelp } from "./command-help.js";
|
|
8
8
|
/** Every WireCat tool, as "Works with" names it; a tool's own entry is left out of its screen. */
|
|
9
9
|
export const WIRECAT_TOOLS = [
|
|
10
10
|
{ command: "tg", does: "Telegram in the same local store: search, review and tasks offline" },
|
|
@@ -56,7 +56,7 @@ export const firstScreen = (program, screen = {}) => {
|
|
|
56
56
|
const can = (screen.can ?? []).map(({ command, does }) => [command, does]);
|
|
57
57
|
const works = WIRECAT_TOOLS.filter((tool) => tool.command !== cli).map(({ command, shown, does }) => [shown ?? command, does]);
|
|
58
58
|
const column = Math.max(22, widest([...can, ...works].map(([left]) => left))) + 2;
|
|
59
|
-
const start = screen.start ?? (commandAt(program, "setup") ? `${cli}
|
|
59
|
+
const start = screen.start ?? (commandAt(program, "setup") ? `${cli} ${EXAMPLE_PROFILE} setup` : undefined);
|
|
60
60
|
return [
|
|
61
61
|
wrapAnsi(`${cli} — ${pitch}`, WIDTH),
|
|
62
62
|
"",
|
|
@@ -95,7 +95,7 @@ export const installHelp = (program, screen = {}) => {
|
|
|
95
95
|
? firstScreen(program, screen)
|
|
96
96
|
: (own.formatHelp ?? Help.prototype.formatHelp).call(helper, command, helper),
|
|
97
97
|
});
|
|
98
|
-
installCommandHelp(program);
|
|
98
|
+
installCommandHelp(program, { profile: screen.profile !== false });
|
|
99
99
|
program.addOption(new Option("--help-all", "the full help: every command and option").hideHelp());
|
|
100
100
|
program.on("option:help-all", () => {
|
|
101
101
|
full = true;
|
|
@@ -12,6 +12,7 @@ import { recalledAccount } from "./accounts.js";
|
|
|
12
12
|
import { photoHelp, readProfilePhoto } from "./admin-commands.js";
|
|
13
13
|
import { accountAnswer, renderItem, wholeList } from "./chat-answers.js";
|
|
14
14
|
import { messengerContext } from "./context.js";
|
|
15
|
+
import { hideNever } from "./lacks.js";
|
|
15
16
|
import { privacyCommand } from "./records-command.js";
|
|
16
17
|
export const accountCommand = (messenger) => {
|
|
17
18
|
const tool = `${messenger.app.command} personal`;
|
|
@@ -80,5 +81,5 @@ export const accountCommand = (messenger) => {
|
|
|
80
81
|
.addCommand(accountSessionsCommand(messenger));
|
|
81
82
|
if (messenger.privacy === true)
|
|
82
83
|
account.addCommand(privacyCommand(messenger));
|
|
83
|
-
return account;
|
|
84
|
+
return hideNever(messenger, account);
|
|
84
85
|
};
|
|
@@ -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 { ADMIN_RIGHTS, GROUP_SETTINGS, } from "../../domain/models.js";
|
|
6
6
|
import { readUpload, typesText } from "../../sends/upload.js";
|
|
@@ -22,19 +22,19 @@ const SETTING_FLAGS = {
|
|
|
22
22
|
onlyAdminsSend: ["only-admins-send <on|off>", "only admins may send messages"],
|
|
23
23
|
onlyAdminsEditInfo: ["only-admins-edit-info <on|off>", "only admins may change the name, photo and description"],
|
|
24
24
|
disappearingSeconds: [
|
|
25
|
-
"disappear-
|
|
25
|
+
"disappear-after <duration|off>",
|
|
26
26
|
"new messages disappear this long after sending: 24h, 7d, 90d; off keeps them",
|
|
27
27
|
],
|
|
28
28
|
};
|
|
29
29
|
const flagOf = (key) => `--${SETTING_FLAGS[key][0].split(" ")[0]}`;
|
|
30
|
-
/** Where commander puts the flag's value: `--disappear-
|
|
30
|
+
/** Where commander puts the flag's value: `--disappear-after` is `disappearAfter`, not the setting's own name. */
|
|
31
31
|
const optionKey = (key) => flagOf(key)
|
|
32
32
|
.slice(2)
|
|
33
33
|
.replace(/-(\w)/g, (_, letter) => letter.toUpperCase());
|
|
34
34
|
const settingsOf = (keys, options) => {
|
|
35
35
|
const changes = {};
|
|
36
36
|
for (const key of keys) {
|
|
37
|
-
const value = options[optionKey(key)];
|
|
37
|
+
const value = options[optionKey(key)] ?? (key === "disappearingSeconds" ? options.disappearTime : undefined);
|
|
38
38
|
if (value === undefined)
|
|
39
39
|
continue;
|
|
40
40
|
if (key === "disappearingSeconds") {
|
|
@@ -67,8 +67,16 @@ const updateCommand = (messenger) => {
|
|
|
67
67
|
update.option("--photo <file>", `a new photo for it — ${photoHelp(messenger)}`);
|
|
68
68
|
if (messenger.groupPhotoRemoval === true)
|
|
69
69
|
update.option("--remove-photo", "take its photo away; not with --photo");
|
|
70
|
-
for (const key of keys)
|
|
71
|
-
|
|
70
|
+
for (const key of keys) {
|
|
71
|
+
const option = new Option(`--${SETTING_FLAGS[key][0]}`, SETTING_FLAGS[key][1]);
|
|
72
|
+
if (key === "disappearingSeconds")
|
|
73
|
+
option.conflicts("disappearTime");
|
|
74
|
+
update.addOption(option);
|
|
75
|
+
}
|
|
76
|
+
if (keys.includes("disappearingSeconds"))
|
|
77
|
+
update.addOption(new Option("--disappear-time <duration|off>", "compatibility alias for --disappear-after")
|
|
78
|
+
.hideHelp()
|
|
79
|
+
.conflicts("disappearAfter"));
|
|
72
80
|
return update.action(async function (chat) {
|
|
73
81
|
const context = messengerContext(this, messenger);
|
|
74
82
|
const { title, description, photo, removePhoto, ...rest } = this.opts();
|
|
@@ -210,6 +218,7 @@ const requestsCommand = (messenger) => {
|
|
|
210
218
|
renderAnswer(this, context, body, lines(({ person_id, name, username, requested_at }) => [person_id, name ?? "", username ? `@${username}` : "", requested_at ?? ""].join(" ")));
|
|
211
219
|
}));
|
|
212
220
|
const everyone = messenger.joinRequestsAll !== false;
|
|
221
|
+
const byLink = everyone && messenger.joinRequestsByLink !== false;
|
|
213
222
|
for (const [verb, accept, help] of [
|
|
214
223
|
["accept", true, "let them in; the group sees them join"],
|
|
215
224
|
["decline", false, "turn the request away"],
|
|
@@ -219,9 +228,9 @@ const requestsCommand = (messenger) => {
|
|
|
219
228
|
.argument("<chat>", messenger.chatArgument)
|
|
220
229
|
.argument(everyone ? "[person]" : "<person>", "who asked: an id from `chats requests list`");
|
|
221
230
|
if (everyone)
|
|
222
|
-
answer
|
|
223
|
-
|
|
224
|
-
|
|
231
|
+
answer.option("--all", "every pending request, counted against the hourly limit first");
|
|
232
|
+
if (byLink)
|
|
233
|
+
answer.option("--link <link>", "with --all: only the requests made by this invite link");
|
|
225
234
|
requests.addCommand(answer.action(async function (chat, person) {
|
|
226
235
|
const context = messengerContext(this, messenger);
|
|
227
236
|
const { all, link } = this.opts();
|
|
@@ -64,7 +64,7 @@ export const storeCommand = (messenger) => {
|
|
|
64
64
|
* What the local store holds, per chat — read from the store alone. Whether a fetch reached a
|
|
65
65
|
* chat's very first message is not recorded, so it is not claimed: the stretches held are shown.
|
|
66
66
|
*/
|
|
67
|
-
const statusCommand = (messenger) => annotate(new Command("status"), { effects: LOCAL_READ
|
|
67
|
+
const statusCommand = (messenger) => annotate(new Command("status"), { effects: LOCAL_READ })
|
|
68
68
|
.summary("what the local archive holds and what is missing")
|
|
69
69
|
.description("per chat: messages stored, the oldest and newest, and the stretches held completely")
|
|
70
70
|
.argument("[chat]", messenger.chatArgument)
|
|
@@ -132,7 +132,7 @@ const clearCommand = (messenger) => annotate(new Command("clear"), { effects: LO
|
|
|
132
132
|
* `--format markdown` for a transcript a person reads. With `--output-dir`, any number of chats into a
|
|
133
133
|
* folder, and each later run into the same folder adds only what changed.
|
|
134
134
|
*/
|
|
135
|
-
const exportCommand = (messenger) => destroysNothing(annotate(new Command("export"), { effects: LOCAL_WRITE
|
|
135
|
+
const exportCommand = (messenger) => destroysNothing(annotate(new Command("export"), { effects: LOCAL_WRITE }))
|
|
136
136
|
.summary("export stored messages to files")
|
|
137
137
|
.description("a chat's stored messages, oldest first: --jsonl one message per line, --json the list envelope of messages; never asks the messenger")
|
|
138
138
|
.argument("[chat...]", `${messenger.chatArgument}; several with --output-dir`)
|
|
@@ -237,7 +237,7 @@ export const jobsCommand = (messenger) => {
|
|
|
237
237
|
: await context.withServices((services) => services.archive.held(chatId)).catch(() => undefined);
|
|
238
238
|
renderAnswer(this, context, itemOf(snakeKeys({ ...jobDetails(job), ...(held ? { held } : {}), log: job.log })));
|
|
239
239
|
});
|
|
240
|
-
destroysNothing(annotate(command.command("cancel"), { effects: LOCAL_WRITE
|
|
240
|
+
destroysNothing(annotate(command.command("cancel"), { effects: LOCAL_WRITE }))
|
|
241
241
|
.summary("cancel a queued or running archive job")
|
|
242
242
|
.description("stop a running background job after its current page; a later sync resumes where it stopped")
|
|
243
243
|
.argument("<job>", "the job id")
|
|
@@ -251,7 +251,7 @@ export const jobsCommand = (messenger) => {
|
|
|
251
251
|
process.kill(job.pid, "SIGTERM");
|
|
252
252
|
renderAnswer(this, context, itemOf({ job: job.id, cancelled: true }));
|
|
253
253
|
});
|
|
254
|
-
destroysNothing(annotate(command.command("retry"), { effects: LOCAL_WRITE
|
|
254
|
+
destroysNothing(annotate(command.command("retry"), { effects: LOCAL_WRITE }))
|
|
255
255
|
.summary("retry an archive job")
|
|
256
256
|
.description("start a partial, failed or died job again, as a new job; the sync resumes where the store stopped")
|
|
257
257
|
.argument("[job]", "the job id")
|
|
@@ -292,7 +292,7 @@ export const jobsCommand = (messenger) => {
|
|
|
292
292
|
if (items.length === 0 && context.format === "pretty")
|
|
293
293
|
context.renderer.note("no partial, failed or died jobs to retry");
|
|
294
294
|
});
|
|
295
|
-
destroysNothing(annotate(command.command("clear"), { effects: LOCAL_WRITE
|
|
295
|
+
destroysNothing(annotate(command.command("clear"), { effects: LOCAL_WRITE }))
|
|
296
296
|
.description("forget finished jobs and remove their logs; a running job is kept")
|
|
297
297
|
.action(function () {
|
|
298
298
|
const context = messengerContext(this, messenger);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Meta } from "@wirecat/cli-base/standards";
|
|
2
|
-
import type { Inbox, Review } from "../../domain/models.js";
|
|
2
|
+
import type { ChatKind, Inbox, Review } from "../../domain/models.js";
|
|
3
3
|
import { type Hearing } from "../../speech/hearing.js";
|
|
4
4
|
import type { AccountKey } from "../../store/store.js";
|
|
5
5
|
import { type QueryWords } from "../list-query.js";
|
|
@@ -11,6 +11,7 @@ export declare const REVIEW_QUERY: import("@wirecat/cli-base/query").QueryRegist
|
|
|
11
11
|
export declare const catchUpQuery: (words: QueryWords, registry: typeof INBOX_QUERY, command: string) => {
|
|
12
12
|
since?: number;
|
|
13
13
|
chat?: string;
|
|
14
|
+
kinds?: ChatKind[];
|
|
14
15
|
meta?: Partial<Meta>;
|
|
15
16
|
};
|
|
16
17
|
export declare const inboxAnswer: (inbox: Inbox, hearing: Hearing | undefined, reader: AccountKey, transcribe: boolean, meta?: Partial<Meta>) => import("../envelope.js").ListBody<{
|
|
@@ -49,6 +50,8 @@ export declare const inboxAnswer: (inbox: Inbox, hearing: Hearing | undefined, r
|
|
|
49
50
|
}[][] | undefined;
|
|
50
51
|
}[];
|
|
51
52
|
}>;
|
|
53
|
+
/** What `review --offline` cannot see. */
|
|
54
|
+
export declare const OFFLINE_REVIEW = "offline: only what was already synced to the local store; run review without --offline for the rest";
|
|
52
55
|
/**
|
|
53
56
|
* A review the next one goes on from: from `until` when nothing was left out, else from the same start again.
|
|
54
57
|
* Saved points (`--new`) carry this themselves, and an `--unanswered` look is not a review to continue.
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { defineQuery, queryError } from "@wirecat/cli-base/query";
|
|
2
|
+
import { kindsOf } from "../../services/inbox.js";
|
|
2
3
|
import { heard, hearingMissing } from "../../speech/hearing.js";
|
|
3
4
|
import { listOf } from "../envelope.js";
|
|
4
5
|
import { listQuery, nativeField, nativeTerms, timeBounds, typedQuery } from "../list-query.js";
|
|
5
6
|
import { nextStep } from "../steps.js";
|
|
6
7
|
import { messageItem } from "./message-items.js";
|
|
7
8
|
const date = nativeField({ name: "date", type: "time", operators: ["range"], example: "date>2h" });
|
|
9
|
+
const kind = nativeField({ name: "kind", type: "kw", operators: ["term", "phrase"], example: "kind:group,channel" });
|
|
8
10
|
/** Where `inbox` starts reading: everything after a time. */
|
|
9
|
-
export const INBOX_QUERY = defineQuery({ entity: "message", backend: "native", fields: [date] });
|
|
11
|
+
export const INBOX_QUERY = defineQuery({ entity: "message", backend: "native", fields: [date, kind] });
|
|
10
12
|
/** Where `review` starts, and the one chat it reads. */
|
|
11
13
|
export const REVIEW_QUERY = defineQuery({
|
|
12
14
|
entity: "message",
|
|
13
15
|
backend: "native",
|
|
14
|
-
fields: [date, nativeField({ name: "chat", type: "kw", operators: ["term", "phrase"], example: "chat:7" })],
|
|
16
|
+
fields: [date, kind, nativeField({ name: "chat", type: "kw", operators: ["term", "phrase"], example: "chat:7" })],
|
|
15
17
|
});
|
|
16
18
|
/** The query's start and chat. A catch-up reads forward from one point, so an end is refused. */
|
|
17
19
|
export const catchUpQuery = (words, registry, command) => {
|
|
@@ -23,9 +25,11 @@ export const catchUpQuery = (words, registry, command) => {
|
|
|
23
25
|
command,
|
|
24
26
|
});
|
|
25
27
|
const chat = terms.get("chat")?.value;
|
|
28
|
+
const kinds = terms.get("kind")?.value;
|
|
26
29
|
return {
|
|
27
30
|
...(since === undefined ? {} : { since }),
|
|
28
31
|
...(chat === undefined ? {} : { chat }),
|
|
32
|
+
...(kinds === undefined ? {} : { kinds: kindsOf(kinds, "kind:") }),
|
|
29
33
|
...(meta === undefined ? {} : { meta }),
|
|
30
34
|
};
|
|
31
35
|
};
|
|
@@ -48,7 +52,7 @@ const nameOf = (chat) => chat.title ?? chat.id;
|
|
|
48
52
|
* What every catch-up answer shares: the messages, oldest first, each naming its chat; what was left out as
|
|
49
53
|
* `missing`; and a chat cut short as a `messages.list` step, after the steps the command puts first.
|
|
50
54
|
*/
|
|
51
|
-
const catchUpList = (command, found, hearing, reader, { transcribe, first = [], meta = {} }) => {
|
|
55
|
+
const catchUpList = (command, found, hearing, reader, { transcribe, first = [], meta = {}, left = [], }) => {
|
|
52
56
|
const read = found.chats
|
|
53
57
|
.flatMap((chat) => heard(chat.messages, hearing).map((message) => ({ ...message, chatTitle: chat.title })))
|
|
54
58
|
.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
@@ -70,6 +74,7 @@ const catchUpList = (command, found, hearing, reader, { transcribe, first = [],
|
|
|
70
74
|
? [{ field: "items", why: `${found.quiet} muted or archived chats left out`, step: `${command}.all` }]
|
|
71
75
|
: []),
|
|
72
76
|
...hearingMissing(hearing, transcribe),
|
|
77
|
+
...left,
|
|
73
78
|
];
|
|
74
79
|
return listOf(command, {
|
|
75
80
|
items: read.map((message) => summaryOf(message, reader)),
|
|
@@ -81,6 +86,8 @@ const catchUpList = (command, found, hearing, reader, { transcribe, first = [],
|
|
|
81
86
|
});
|
|
82
87
|
};
|
|
83
88
|
export const inboxAnswer = (inbox, hearing, reader, transcribe, meta = {}) => catchUpList("inbox", inbox, hearing, reader, { transcribe, meta });
|
|
89
|
+
/** What `review --offline` cannot see. */
|
|
90
|
+
export const OFFLINE_REVIEW = "offline: only what was already synced to the local store; run review without --offline for the rest";
|
|
84
91
|
const quoted = (value) => `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
85
92
|
/**
|
|
86
93
|
* A review the next one goes on from: from `until` when nothing was left out, else from the same start again.
|
|
@@ -95,6 +102,7 @@ export const reviewAnswer = (found, hearing, reader, { transcribe, fresh, chat,
|
|
|
95
102
|
return catchUpList("review", found, hearing, reader, {
|
|
96
103
|
transcribe,
|
|
97
104
|
first: continues ? [nextStep("review.next", { args: { query: query.join(" ") } })] : [],
|
|
105
|
+
...(found.offline ? { left: [{ field: "items", why: OFFLINE_REVIEW }] } : {}),
|
|
98
106
|
...(meta === undefined ? {} : { meta }),
|
|
99
107
|
});
|
|
100
108
|
};
|
|
@@ -19,6 +19,7 @@ import { membersCommand } from "./chats-members-command.js";
|
|
|
19
19
|
import { markReadCommand } from "./chats-read-command.js";
|
|
20
20
|
import { trackingCommand } from "./chats-tracking-command.js";
|
|
21
21
|
import { messengerContext } from "./context.js";
|
|
22
|
+
import { hideNever } from "./lacks.js";
|
|
22
23
|
import { archiveCommands, chatDeletionCommands, chatPinCommands, markUnreadCommand, mediaCommand, muteCommands, } from "./records-command.js";
|
|
23
24
|
export const chatsCommand = (messenger) => {
|
|
24
25
|
const chats = new Command("chats").description("the account's chats");
|
|
@@ -153,7 +154,7 @@ export const chatsCommand = (messenger) => {
|
|
|
153
154
|
if (command)
|
|
154
155
|
annotate(command, { examples: [`${messenger.app.command} personal chats ${path} ${args}`.trimEnd()] });
|
|
155
156
|
}
|
|
156
|
-
return chats;
|
|
157
|
+
return hideNever(messenger, chats);
|
|
157
158
|
};
|
|
158
159
|
/** Where the history is pushed, the chat list and a chat's card answer from the store and never connect. */
|
|
159
160
|
const storedRead = (messenger) => (messenger.history === "store" ? LOCAL_READ : REMOTE_READ);
|
|
@@ -14,7 +14,7 @@ export const statsCommand = (messenger) => annotate(new Command("show"), { effec
|
|
|
14
14
|
`joins and leaves — counted from the local store; ${messenger.history === "store" ? "joins and leaves too" : "joins and leaves are asked of the messenger"}`)
|
|
15
15
|
.argument("<chat>", messenger.chatArgument)
|
|
16
16
|
.option("--since-time <time>", `ISO 8601, or 2h / 1d ago; ${EVENTS_DAYS} days ago if not given`)
|
|
17
|
-
.option("--by <
|
|
17
|
+
.option("--by <group>", "also one row per calendar day or week (weeks start on Monday)", periodOf)
|
|
18
18
|
.option("--timezone <zone>", "the IANA timezone for calendar days")
|
|
19
19
|
.action(async function (chat) {
|
|
20
20
|
const context = messengerContext(this, messenger);
|
|
@@ -19,7 +19,11 @@ const SHELLS = ["zsh", "bash", "fish", "powershell"];
|
|
|
19
19
|
* ⚠ **A Tab never connects and never creates anything.** Names come from the message store if it
|
|
20
20
|
* exists — no store, no names — and nothing goes to stderr: a shell shows whatever it is given.
|
|
21
21
|
*/
|
|
22
|
-
export const completeCommand = (messenger, config, options = {}) => annotate(new Command("complete"), {
|
|
22
|
+
export const completeCommand = (messenger, config, options = {}) => annotate(new Command("complete"), {
|
|
23
|
+
effects: LOCAL_READ,
|
|
24
|
+
mcp: false,
|
|
25
|
+
examples: [`${messenger.app.command} complete zsh`],
|
|
26
|
+
})
|
|
23
27
|
.description(`shell completion: \`${messenger.app.command} complete zsh\` prints the script to source`)
|
|
24
28
|
.argument("[words...]", "zsh for the shell script, or command words to complete")
|
|
25
29
|
.allowUnknownOption()
|
|
@@ -19,6 +19,7 @@ import { commandAt, nextStep } from "../steps.js";
|
|
|
19
19
|
import { contactWriteCommands } from "./admin-contacts-command.js";
|
|
20
20
|
import { contactAnswer, lines, pageOf } from "./chat-answers.js";
|
|
21
21
|
import { messengerContext } from "./context.js";
|
|
22
|
+
import { hideNever } from "./lacks.js";
|
|
22
23
|
import { CONTACTS_QUERY, notesAllowed } from "./people-queries.js";
|
|
23
24
|
import { privatePeopleCommands } from "./private-people-command.js";
|
|
24
25
|
import { syncsFirst } from "./search-sync-options.js";
|
|
@@ -296,8 +297,8 @@ export const contactsCommand = (messenger) => {
|
|
|
296
297
|
link: '"Alex Example" zoom:101',
|
|
297
298
|
list: "--limit 10",
|
|
298
299
|
lookup: "",
|
|
299
|
-
"notes add": '"Alex Example" --file ./note.txt',
|
|
300
|
-
"notes edit": '"Alex Example" 1 --revision 1 --file ./note.txt',
|
|
300
|
+
"notes add": '"Alex Example" --text-file ./note.txt',
|
|
301
|
+
"notes edit": '"Alex Example" 1 --revision 1 --text-file ./note.txt',
|
|
301
302
|
"notes list": '"Alex Example"',
|
|
302
303
|
"notes remove": '"Alex Example" 1',
|
|
303
304
|
"notes show": '"Alex Example" 1',
|
|
@@ -315,5 +316,5 @@ export const contactsCommand = (messenger) => {
|
|
|
315
316
|
if (command)
|
|
316
317
|
annotate(command, { examples: [`${messenger.app.command} personal contacts ${path} ${args}`.trimEnd()] });
|
|
317
318
|
}
|
|
318
|
-
return contacts;
|
|
319
|
+
return hideNever(messenger, contacts);
|
|
319
320
|
};
|
|
@@ -95,6 +95,11 @@ export interface Messenger extends ServiceMessenger {
|
|
|
95
95
|
* connecting, as the adapter would refuse after; one connection per device makes that connection a cost.
|
|
96
96
|
*/
|
|
97
97
|
lacks?: readonly (keyof MessengerAdapter)[];
|
|
98
|
+
/**
|
|
99
|
+
* Adapter methods this messenger can never have, where `lacks` is what it does not have yet: their commands refuse
|
|
100
|
+
* the same way and are also hidden from help, `commands` and MCP. WhatsApp: only the phone ends other sessions.
|
|
101
|
+
*/
|
|
102
|
+
never?: readonly (keyof MessengerAdapter)[];
|
|
98
103
|
/** Whether a message can be deleted for the owner alone; yes when unset. No: every delete is for everyone (WhatsApp). */
|
|
99
104
|
deleteForMe?: boolean;
|
|
100
105
|
/** Whether the messenger has forum topics: `--topic`, `topic:` in `messages list` and the `topics_*` tools; yes when unset. */
|
|
@@ -153,6 +158,8 @@ export interface Messenger extends ServiceMessenger {
|
|
|
153
158
|
inviteLinkUpdate?: boolean;
|
|
154
159
|
/** Whether every pending join request is answered at once — `chats requests accept --all`; yes when unset. */
|
|
155
160
|
joinRequestsAll?: boolean;
|
|
161
|
+
/** Whether `--all` can take only the requests made by one invite link — `--link`; yes when unset. */
|
|
162
|
+
joinRequestsByLink?: boolean;
|
|
156
163
|
/** Whether a channel post can be commented on — `messages send --comment-to`; yes when unset. */
|
|
157
164
|
channelComments?: boolean;
|
|
158
165
|
/** Whether people added can be shown the messages from before they came — `members add --history`; yes when unset. */
|