@wirecat/cli-core 0.405.0 → 0.406.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 +24 -1
- package/dist/botcheck/reasons.d.ts +1 -1
- package/dist/cli/analysis-choice.js +1 -1
- package/dist/cli/answer-entity.d.ts +5 -0
- package/dist/cli/answer-entity.js +25 -0
- package/dist/cli/command-contract.js +2 -5
- package/dist/cli/commands-command.js +4 -1
- package/dist/cli/effects.js +1 -1
- package/dist/cli/embedding-choice.d.ts +1 -1
- package/dist/cli/embedding-choice.js +2 -2
- package/dist/cli/help/command-help.js +2 -2
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +1 -0
- package/dist/cli/json-fields.js +2 -2
- package/dist/cli/meeting-project-candidates-command.js +0 -1
- package/dist/cli/meeting-series-inventory-command.js +0 -1
- package/dist/cli/messenger/admin-commands.js +14 -14
- package/dist/cli/messenger/archive-commands.js +2 -2
- package/dist/cli/messenger/attachments-command.js +7 -8
- package/dist/cli/messenger/backfill-command.js +1 -1
- package/dist/cli/messenger/chat-answers.js +3 -3
- package/dist/cli/messenger/chat-lists.js +3 -3
- package/dist/cli/messenger/chats-command.js +8 -8
- package/dist/cli/messenger/chats-members-command.js +1 -1
- package/dist/cli/messenger/contacts-command.js +10 -7
- package/dist/cli/messenger/context.d.ts +11 -4
- package/dist/cli/messenger/context.js +6 -0
- package/dist/cli/messenger/conversation-examples.js +7 -6
- package/dist/cli/messenger/conversations-command.d.ts +0 -4
- package/dist/cli/messenger/conversations-command.js +130 -135
- package/dist/cli/messenger/counters-command.js +4 -2
- package/dist/cli/messenger/doctor-command.js +8 -4
- package/dist/cli/messenger/gaps-command.js +2 -2
- package/dist/cli/messenger/hearing-command.d.ts +1 -1
- package/dist/cli/messenger/hearing-command.js +1 -1
- package/dist/cli/messenger/images-command.js +1 -1
- package/dist/cli/messenger/inbox.js +1 -3
- package/dist/cli/messenger/lacks.d.ts +1 -0
- package/dist/cli/messenger/lacks.js +1 -1
- package/dist/cli/messenger/mcp-command.js +4 -4
- package/dist/cli/messenger/messages-comments-command.js +10 -5
- package/dist/cli/messenger/metadata-command.js +5 -5
- package/dist/cli/messenger/models-command.js +101 -90
- package/dist/cli/messenger/private-people-command.js +5 -6
- package/dist/cli/messenger/replies-edit-command.js +0 -1
- package/dist/cli/messenger/review.js +1 -3
- package/dist/cli/messenger/serve-relay.d.ts +1 -0
- package/dist/cli/messenger/stats-command.js +1 -1
- package/dist/cli/messenger/store-maintenance-command.d.ts +2 -2
- package/dist/cli/messenger/store-maintenance-command.js +4 -4
- package/dist/cli/messenger/stored.js +5 -0
- package/dist/cli/messenger/tags-command.js +4 -10
- package/dist/cli/messenger/tasks-command.js +3 -3
- package/dist/cli/messenger/transcribe-command.js +1 -1
- package/dist/cli/moved.d.ts +2 -0
- package/dist/cli/moved.js +39 -1
- package/dist/cli/permission-keys.js +2 -0
- package/dist/cli/settings.js +1 -1
- package/dist/cli/steps.js +4 -4
- package/dist/cli/store-settings.js +1 -1
- package/dist/cli/upgrade-command.js +0 -1
- package/dist/domain/models.d.ts +3 -3
- package/dist/domain/task-categories.js +1 -1
- package/dist/embeddings/remote.js +1 -1
- package/dist/entities/member.d.ts +2 -2
- package/dist/entities/member.js +2 -2
- package/dist/mcp/agent.d.ts +1 -0
- package/dist/mcp/agent.js +1 -1
- package/dist/mcp/tools/attachments.js +1 -1
- package/dist/meetings/mcp/index.d.ts +15 -15
- package/dist/ports/app.d.ts +1 -1
- package/dist/ports/messenger.d.ts +6 -1
- package/dist/search/task-similarity.js +1 -1
- package/dist/sends/guard.js +2 -2
- package/dist/sends/permission-aliases.d.ts +5 -0
- package/dist/sends/permission-aliases.js +49 -0
- package/dist/sends/permissions.js +33 -25
- package/dist/services/admin.js +7 -7
- package/dist/services/chats.js +1 -1
- package/dist/services/conversations.d.ts +1 -1
- package/dist/services/conversations.js +1 -1
- package/dist/services/counters.js +1 -1
- package/dist/services/embeddings-maintenance.js +2 -2
- package/dist/services/embeddings-search.js +1 -1
- package/dist/services/embeddings.d.ts +4 -4
- package/dist/services/messages-search.d.ts +1 -1
- package/dist/services/messages-search.js +2 -2
- package/dist/services/metadata.js +2 -2
- package/dist/services/notes-search.js +3 -3
- package/dist/services/search-catchup.js +1 -1
- package/dist/speech/install.d.ts +4 -2
- package/dist/speech/install.js +16 -4
- package/dist/speech/local.js +1 -1
- package/dist/speech/transcribe.js +1 -1
- package/dist/store/search-index.d.ts +1 -1
- package/dist/store/sqlite/attachment-texts.d.ts +1 -1
- package/dist/store/sqlite/attachment-texts.js +1 -1
- package/dist/store/sqlite/batches.js +1 -1
- package/dist/store/sqlite/involvements.d.ts +1 -1
- package/dist/store/sqlite/note-index.d.ts +2 -2
- package/dist/store/sqlite/note-index.js +2 -2
- package/dist/store/sqlite/search-index.d.ts +1 -1
- package/dist/store/sqlite/search-index.js +1 -1
- package/dist/store/sqlite/stems-fill.d.ts +3 -3
- package/dist/store/sqlite/stems-fill.js +3 -3
- package/dist/store/sqlite/stems.d.ts +1 -1
- package/dist/store/sqlite/task-pairs.d.ts +1 -1
- package/dist/store/sqlite/task-pairs.js +1 -1
- package/package.json +1 -1
- package/parity.json +130 -139
- package/skills/follow-ups/SKILL.md +5 -5
- package/skills/link-conversations/SKILL.md +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,30 @@ Notable changes to `@wirecat/cli-core` (`@wirecat/cli-messaging` up to 0.261.0,
|
|
|
5
5
|
minor version may break callers, and says how under "Changed — may break callers". `pnpm docs:check` checks the
|
|
6
6
|
shape of this file.
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 0.406.0 — 24.10.2026
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `StoredMessages.threadMessages` lets an adapter read a provider thread from the shared message store without
|
|
13
|
+
keeping another copy. `HistoryBatch.deletions` applies chat-scoped tombstones from synced snapshots without
|
|
14
|
+
replaying those deletions as live events. `Messenger.readsComments` allows comment reads independently of
|
|
15
|
+
comment sending; unset, it keeps the existing capability behavior.
|
|
16
|
+
- Speech model installation can verify existing files before reusing them (`verifyExisting`), so repair uses the shared checksum and download path.
|
|
17
|
+
- Provider adapters can declare a typed answer with `answerEntity`, shared by command schemas, help and JSON field validation without changing a shared entity.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Finite meeting inventory, project scanning and upgrade commands are available to the shared MCP catalogue. Profile permissions still apply; upgrade retains its authority grant.
|
|
22
|
+
|
|
23
|
+
### Changed — may break callers
|
|
24
|
+
|
|
25
|
+
- **Only canonical option spellings are accepted.** The hidden flag aliases introduced in 0.399.0 are removed: use the analysis/embedding/consent provider flags, `--claim`, `--answer-model`, `--template-mode`, `--text-file`, `--disappear-after` and the `kind:` query term. Retired flags are refused before command work.
|
|
26
|
+
|
|
27
|
+
- **Shared command paths use the vocabulary.** Contacts use `info`, `events` and `aliases remove`; chat invite links use `links remove`, member checks use `check`, and join requests use `confirm` or `reject`. Metadata uses `show` and `sync`; task edits use `update`; derived tags and search indexes use `rebuild`. Discovery uses `commands info`; MCP configuration uses `mcp add` and `mcp check`.
|
|
28
|
+
|
|
29
|
+
- **Explicit model and conversation commands.** Use `models list kind:audio` or `kind:text`, `models download <model>` and `models keys set/remove`. Conversation link construction is `conversations links rebuild`; vector reports and removal are `conversations embeddings status/clear`, while `conversations embed` still computes vectors. Duplicate candidates are `tasks duplicates list`. Report information is `doctor reports show`; `doctor reports create` writes the report. Bare groups only offer help. Retired spellings are refused with guidance to the canonical path; no compatibility command aliases are mounted. CLI and MCP use the same paths.
|
|
30
|
+
|
|
31
|
+
- **Rename moved permission paths.** A saved key or startup override using a renamed path now stops with `configuration_error` and its exact replacement (including bot keys), before opening the store or performing the action. Canonical commands keep the migrated denial; retired CLI spellings cannot run.
|
|
9
32
|
|
|
10
33
|
## 0.405.0 — 24.10.2026
|
|
11
34
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** What `chats members
|
|
1
|
+
/** What `chats members check` judges from a member list and the store. */
|
|
2
2
|
export type AuditReason = "bot" | "scam" | "fake" | "deleted" | "no_photo" | "no_username" | "odd_name" | "never_wrote" | "link_first" | "burst_join" | "mass_invited";
|
|
3
3
|
/** What only a look at one person adds: their profile, all their stored messages, the registries. */
|
|
4
4
|
export type PersonReason = "new_account" | "no_bio" | "same_text" | "photo_recent" | RegistryReason;
|
|
@@ -35,6 +35,6 @@ export const analysisChoice = (options, app, settings, env, keyring) => {
|
|
|
35
35
|
const keyName = customUrl === undefined ? provider : endpointKeyName(baseUrl);
|
|
36
36
|
const apiKey = embeddingKeys(app, env, keyring).read(keyName)?.key;
|
|
37
37
|
if (!apiKey && customUrl === undefined)
|
|
38
|
-
throw new CliError("authentication_error", `no ${provider} key — use models
|
|
38
|
+
throw new CliError("authentication_error", `no ${provider} key — use models keys set ${provider}`);
|
|
39
39
|
return { provider, model, baseUrl, ...(apiKey ? { apiKey } : {}) };
|
|
40
40
|
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type * as v from "valibot";
|
|
3
|
+
/** Provider-specific wire fields must not redefine an entity shared by every CLI. */
|
|
4
|
+
export declare const answerEntity: (command: Command, name: string, schema: v.GenericSchema) => Command;
|
|
5
|
+
export declare const answerEntitySchema: (command: Command | undefined) => v.GenericSchema | undefined;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { annotate, metaOf } from "@wirecat/cli-base/commands";
|
|
2
|
+
import { ENTITIES } from "../entities/index.js";
|
|
3
|
+
const schemas = new WeakMap();
|
|
4
|
+
/** Provider-specific wire fields must not redefine an entity shared by every CLI. */
|
|
5
|
+
export const answerEntity = (command, name, schema) => {
|
|
6
|
+
if (Object.hasOwn(ENTITIES, name))
|
|
7
|
+
throw new Error(`${name} is a shared entity; use its existing schema`);
|
|
8
|
+
if (!/^[a-z][a-z0-9_]*$/.test(name))
|
|
9
|
+
throw new Error("an answer entity needs a lowercase name");
|
|
10
|
+
if (!("entries" in schema) && schema.type !== "union")
|
|
11
|
+
throw new Error("an answer entity needs an object schema or a union of object schemas");
|
|
12
|
+
schemas.set(command, { name, schema });
|
|
13
|
+
return annotate(command, { output: { entity: name } });
|
|
14
|
+
};
|
|
15
|
+
export const answerEntitySchema = (command) => {
|
|
16
|
+
const output = command && metaOf(command).output;
|
|
17
|
+
if (!output)
|
|
18
|
+
return undefined;
|
|
19
|
+
const name = output.view ? `${output.entity}.${output.view}` : output.entity;
|
|
20
|
+
const shared = ENTITIES[name];
|
|
21
|
+
if (shared)
|
|
22
|
+
return shared;
|
|
23
|
+
const own = schemas.get(command);
|
|
24
|
+
return own?.name === name ? own.schema : undefined;
|
|
25
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { CliError } from "@wirecat/cli-base";
|
|
2
2
|
import { describeOptions, metaOf } from "@wirecat/cli-base/commands";
|
|
3
3
|
import { jsonSchemaOf } from "@wirecat/cli-base/standards";
|
|
4
|
-
import { ENTITIES } from "../entities/index.js";
|
|
5
4
|
import { keyForCommand } from "../sends/permissions.js";
|
|
5
|
+
import { answerEntitySchema } from "./answer-entity.js";
|
|
6
6
|
import { answerSchema } from "./envelope.js";
|
|
7
7
|
export const SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
|
|
8
8
|
/**
|
|
@@ -10,10 +10,7 @@ export const SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
|
|
|
10
10
|
* A command that declares none answers the same envelopes with items of any shape; `--jsonl` streams the items.
|
|
11
11
|
*/
|
|
12
12
|
export const outputOf = (command) => {
|
|
13
|
-
const
|
|
14
|
-
const entity = output
|
|
15
|
-
? ENTITIES[(output.view ? `${output.entity}.${output.view}` : output.entity)]
|
|
16
|
-
: undefined;
|
|
13
|
+
const entity = answerEntitySchema(command);
|
|
17
14
|
const { $schema: _, ...schema } = jsonSchemaOf(answerSchema(entity));
|
|
18
15
|
const { $schema: __, ...item } = (entity ? jsonSchemaOf(entity) : {});
|
|
19
16
|
const coverage = entity ? "declared-entity" : "envelope-with-open-items";
|
|
@@ -50,7 +50,10 @@ 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("
|
|
53
|
+
command.addCommand(annotate(new Command("info"), {
|
|
54
|
+
effects: NO_EFFECTS,
|
|
55
|
+
examples: [`${app.command} commands info messages list`],
|
|
56
|
+
})
|
|
54
57
|
.description("one command's argv and result schemas, effects, permissions and retry guidance")
|
|
55
58
|
.argument("<path...>", "one command path, for example: stats messages show")
|
|
56
59
|
.action(function (path) {
|
package/dist/cli/effects.js
CHANGED
|
@@ -124,7 +124,7 @@ export const ownerOnlyOption = (flags, description) => annotateOption(new Option
|
|
|
124
124
|
export const judgedEffects = (effects) => effects && { remote: effects.remote, local: "none" };
|
|
125
125
|
/**
|
|
126
126
|
* An unasked step is judged stricter than a retry: only a fetch's local write is incidental. A command that reads
|
|
127
|
-
* nothing remotely and writes here (`server restart`, `store
|
|
127
|
+
* nothing remotely and writes here (`server restart`, `store rebuild`) is the whole point of the step, so it waits
|
|
128
128
|
* to be asked (RISK-1211).
|
|
129
129
|
*/
|
|
130
130
|
const judgedStepEffects = (effects) => effects?.remote === "none" ? effects : judgedEffects(effects);
|
|
@@ -10,7 +10,7 @@ export interface ModelOptions {
|
|
|
10
10
|
dims?: number;
|
|
11
11
|
concurrency?: number;
|
|
12
12
|
}
|
|
13
|
-
/** A local model id, or a remote model with its key from `models
|
|
13
|
+
/** A local model id, or a remote model with its key from `models keys set`. */
|
|
14
14
|
export declare const embeddingChoice: ({ model, provider, embeddingProvider, baseUrl, dims, concurrency }: ModelOptions, app: AppIdentity, settings: AISettings, env: NodeJS.ProcessEnv, { needKey, keyring }?: {
|
|
15
15
|
needKey?: boolean;
|
|
16
16
|
keyring?: KeyringStore | undefined;
|
|
@@ -5,7 +5,7 @@ import { DEFAULT_TEXT_MODEL } from "../embeddings/models.js";
|
|
|
5
5
|
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
|
-
/** A local model id, or a remote model with its key from `models
|
|
8
|
+
/** A local model id, or a remote model with its key from `models keys set`. */
|
|
9
9
|
export const embeddingChoice = ({ model, provider, embeddingProvider, baseUrl, dims, concurrency }, app, settings, env, { needKey = true, keyring } = {}) => {
|
|
10
10
|
if (embeddingProvider !== undefined && provider !== undefined)
|
|
11
11
|
throw new CliError("validation_error", "give --embedding-provider or its legacy --provider spelling, not both");
|
|
@@ -34,7 +34,7 @@ export const embeddingChoice = ({ model, provider, embeddingProvider, baseUrl, d
|
|
|
34
34
|
const keyName = baseUrl === undefined ? "openai" : endpointKeyName(baseUrl);
|
|
35
35
|
const key = embeddingKeys(app, env, keyring).read(keyName)?.key;
|
|
36
36
|
if (!key && needKey && baseUrl === undefined) {
|
|
37
|
-
throw new CliError("authentication_error", `no OpenAI key — \`${typedCommand(app.command, "models
|
|
37
|
+
throw new CliError("authentication_error", `no OpenAI key — \`${typedCommand(app.command, "models keys set openai")}\`, or OPENAI_API_KEY`);
|
|
38
38
|
}
|
|
39
39
|
return { remote, ...(key ? { apiKey: key } : {}), ...(concurrency ? { concurrency } : {}) };
|
|
40
40
|
};
|
|
@@ -112,8 +112,8 @@ const returnsOf = (command) => {
|
|
|
112
112
|
const noun = (output.view ? `${output.entity} ${output.view}` : output.entity).replaceAll("_", " ");
|
|
113
113
|
const more = fields.length > 8 ? `, and ${fields.length - 8} more` : "";
|
|
114
114
|
const root = rootOf(command);
|
|
115
|
-
const schema = commandAt(root, "commands
|
|
116
|
-
? `; every field: ${root.name()} commands
|
|
115
|
+
const schema = commandAt(root, "commands info")
|
|
116
|
+
? `; every field: ${root.name()} commands info ${commandPathOf(command).join(" ")}`
|
|
117
117
|
: "";
|
|
118
118
|
return `${noun} — ${fields.slice(0, 8).join(", ")}${more}${schema}`;
|
|
119
119
|
};
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ export { unanswered } from "../services/questions.js";
|
|
|
33
33
|
export { openRequestTask } from "../services/task-rules.js";
|
|
34
34
|
export { TASK_PAIR_QUERY, TASK_QUERY } from "../services/tasks.js";
|
|
35
35
|
export { type AgentPolicy, type AgentTask, type EvaluationResult, evaluateAgent, type Observation, } from "./agent-evaluation.js";
|
|
36
|
+
export { answerEntity } from "./answer-entity.js";
|
|
36
37
|
export { botAdminsCommand, botMembersCommand } from "./bot/admins.js";
|
|
37
38
|
export { type ApiCommandInput, type ApiCommands, generatedApiCommand } from "./bot/api.js";
|
|
38
39
|
export { apiFlagOf, apiJson, apiOptionKey, apiPlainJson, checkApiBody, checkApiParameter, parseApiJson, readApiBody, } from "./bot/api-input.js";
|
package/dist/cli/index.js
CHANGED
|
@@ -28,6 +28,7 @@ export { unanswered } from "../services/questions.js";
|
|
|
28
28
|
export { openRequestTask } from "../services/task-rules.js";
|
|
29
29
|
export { TASK_PAIR_QUERY, TASK_QUERY } from "../services/tasks.js";
|
|
30
30
|
export { evaluateAgent, } from "./agent-evaluation.js";
|
|
31
|
+
export { answerEntity } from "./answer-entity.js";
|
|
31
32
|
export { botAdminsCommand, botMembersCommand } from "./bot/admins.js";
|
|
32
33
|
export { generatedApiCommand } from "./bot/api.js";
|
|
33
34
|
export { apiFlagOf, apiJson, apiOptionKey, apiPlainJson, checkApiBody, checkApiParameter, parseApiJson, readApiBody, } from "./bot/api-input.js";
|
package/dist/cli/json-fields.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CliError } from "@wirecat/cli-base";
|
|
2
2
|
import { metaOf } from "@wirecat/cli-base/commands";
|
|
3
3
|
import { ENTITIES } from "../entities/index.js";
|
|
4
|
+
import { answerEntitySchema } from "./answer-entity.js";
|
|
4
5
|
const declaredEntity = (command) => {
|
|
5
6
|
const output = command ? metaOf(command).output : undefined;
|
|
6
7
|
return output && (output.view ? `${output.entity}.${output.view}` : output.entity);
|
|
@@ -37,8 +38,7 @@ export const answerFields = (command, resolve) => {
|
|
|
37
38
|
const own = resolve?.(command);
|
|
38
39
|
if (own !== undefined)
|
|
39
40
|
return [...own];
|
|
40
|
-
const
|
|
41
|
-
const schema = name === undefined ? undefined : ENTITIES[name];
|
|
41
|
+
const schema = answerEntitySchema(command);
|
|
42
42
|
return schemaFields(schema);
|
|
43
43
|
};
|
|
44
44
|
const PATHS = /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*(,[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*)*$/;
|
|
@@ -31,7 +31,6 @@ export const addMeetingProjectCandidatesCommand = (program, deps) => {
|
|
|
31
31
|
cursor: options.cursor,
|
|
32
32
|
}, deps.signal()))), {
|
|
33
33
|
origin: "handwritten",
|
|
34
|
-
mcp: false,
|
|
35
34
|
effects: LOCAL_READ,
|
|
36
35
|
output: { entity: "meeting_project_candidates" },
|
|
37
36
|
examples: [`${program.name()} meetings projects scan meeting:1/2 --json`],
|
|
@@ -31,7 +31,6 @@ export const addMeetingSeriesInventoryCommand = (program, deps) => {
|
|
|
31
31
|
origin: "handwritten",
|
|
32
32
|
effects: REMOTE_READ,
|
|
33
33
|
offline: false,
|
|
34
|
-
mcp: false,
|
|
35
34
|
output: { entity: "meeting_series_inventory" },
|
|
36
35
|
examples: [`${program.name()} meetings scheduled --json`],
|
|
37
36
|
});
|
|
@@ -69,14 +69,8 @@ const updateCommand = (messenger) => {
|
|
|
69
69
|
update.option("--remove-photo", "take its photo away; not with --photo");
|
|
70
70
|
for (const key of keys) {
|
|
71
71
|
const option = new Option(`--${SETTING_FLAGS[key][0]}`, SETTING_FLAGS[key][1]);
|
|
72
|
-
if (key === "disappearingSeconds")
|
|
73
|
-
option.conflicts("disappearTime");
|
|
74
72
|
update.addOption(option);
|
|
75
73
|
}
|
|
76
|
-
if (keys.includes("disappearingSeconds"))
|
|
77
|
-
update.addOption(new Option("--disappear-time <duration|off>", "compatibility alias for --disappear-after")
|
|
78
|
-
.hideHelp()
|
|
79
|
-
.conflicts("disappearAfter"));
|
|
80
74
|
return update.action(async function (chat) {
|
|
81
75
|
const context = messengerContext(this, messenger);
|
|
82
76
|
const { title, description, photo, removePhoto, ...rest } = this.opts();
|
|
@@ -92,7 +86,7 @@ const updateCommand = (messenger) => {
|
|
|
92
86
|
});
|
|
93
87
|
};
|
|
94
88
|
const linkCommand = (messenger) => {
|
|
95
|
-
const link = new Command("
|
|
89
|
+
const link = new Command("links").description("a group's invite link");
|
|
96
90
|
annotate(link.command("show"), { output: { entity: "chat_invite" }, effects: REMOTE_READ })
|
|
97
91
|
.description("the invite link, if you may see it")
|
|
98
92
|
.argument("<chat>", messenger.chatArgument)
|
|
@@ -132,10 +126,13 @@ const linkCommand = (messenger) => {
|
|
|
132
126
|
].join(" ")));
|
|
133
127
|
}));
|
|
134
128
|
if (messenger.inviteLinkRevoke !== false)
|
|
135
|
-
link.addCommand(annotate(new Command("
|
|
129
|
+
link.addCommand(annotate(new Command("remove"), {
|
|
130
|
+
output: { entity: "chat_invite_change" },
|
|
131
|
+
effects: REMOTE_WRITE,
|
|
132
|
+
})
|
|
136
133
|
.description("stop one link; for the group's own link, the answer is the new one")
|
|
137
134
|
.argument("<chat>", messenger.chatArgument)
|
|
138
|
-
.argument("<link>", "the link, as `chats
|
|
135
|
+
.argument("<link>", "the link, as `chats links list` shows it")
|
|
139
136
|
.action(async function (chat, link) {
|
|
140
137
|
const context = messengerContext(this, messenger);
|
|
141
138
|
renderItem(this, context, await context.withServices((services) => services.admin.revokeLink(chat, link)));
|
|
@@ -144,7 +141,7 @@ const linkCommand = (messenger) => {
|
|
|
144
141
|
link.addCommand(annotate(new Command("update"), { output: { entity: "chat_invite_change" }, effects: REMOTE_WRITE })
|
|
145
142
|
.description("change one of your invite links, the group's own one too")
|
|
146
143
|
.argument("<chat>", messenger.chatArgument)
|
|
147
|
-
.argument("<link>", "the link, as `chats
|
|
144
|
+
.argument("<link>", "the link, as `chats links list` shows it")
|
|
148
145
|
.option("--approval", "who joins by it asks first, and an admin lets them in; it then has no use limit")
|
|
149
146
|
.option("--no-approval", "anyone with it joins at once")
|
|
150
147
|
.option("--expire-time <time>", "it stops working then: 2026-09-25T09:00 (local time), or 30m, 2h, 7d from now; `never` takes the expiry away")
|
|
@@ -219,11 +216,14 @@ const requestsCommand = (messenger) => {
|
|
|
219
216
|
}));
|
|
220
217
|
const everyone = messenger.joinRequestsAll !== false;
|
|
221
218
|
const byLink = everyone && messenger.joinRequestsByLink !== false;
|
|
222
|
-
for (const [
|
|
223
|
-
[
|
|
224
|
-
[
|
|
219
|
+
for (const [accept, help] of [
|
|
220
|
+
[true, "let them in; the group sees them join"],
|
|
221
|
+
[false, "turn the request away"],
|
|
225
222
|
]) {
|
|
226
|
-
const answer = annotate(new Command(
|
|
223
|
+
const answer = annotate(new Command(accept ? "confirm" : "reject"), {
|
|
224
|
+
output: { entity: "chat_request_change" },
|
|
225
|
+
effects: REMOTE_WRITE,
|
|
226
|
+
})
|
|
227
227
|
.description(help)
|
|
228
228
|
.argument("<chat>", messenger.chatArgument)
|
|
229
229
|
.argument(everyone ? "[person]" : "<person>", "who asked: an id from `chats requests list`");
|
|
@@ -38,7 +38,7 @@ export const storeCommand = (messenger) => {
|
|
|
38
38
|
"copies delete": "messages_copy_1",
|
|
39
39
|
decrypt: "./backup.enc --output ./restored.db",
|
|
40
40
|
export: '"Book club" --output ./messages.jsonl',
|
|
41
|
-
"gaps
|
|
41
|
+
"gaps show": '"Book club"',
|
|
42
42
|
"gaps repair": '"Book club" --limit 100',
|
|
43
43
|
info: "",
|
|
44
44
|
"jobs cancel": "job-101",
|
|
@@ -47,7 +47,7 @@ export const storeCommand = (messenger) => {
|
|
|
47
47
|
"jobs retry": "--failed",
|
|
48
48
|
"jobs show": "",
|
|
49
49
|
migrate: "",
|
|
50
|
-
|
|
50
|
+
rebuild: "",
|
|
51
51
|
repair: "--dry-run",
|
|
52
52
|
reset: "",
|
|
53
53
|
restore: "./backup.db",
|
|
@@ -27,7 +27,7 @@ const count = (value) => {
|
|
|
27
27
|
const line = ({ status, ref, attachment, name, chars }) => `${status} ${ref} #${attachment}${name ? ` ${name}` : ""}${chars === undefined ? "" : ` (${chars} characters)`}`;
|
|
28
28
|
const summary = (run) => [
|
|
29
29
|
`${run.extracted} extracted`,
|
|
30
|
-
run.needsAgent ? `${run.needsAgent} for an agent to read (attachments
|
|
30
|
+
run.needsAgent ? `${run.needsAgent} for an agent to read (attachments update)` : "",
|
|
31
31
|
run.failed ? `${run.failed} unreadable` : "",
|
|
32
32
|
run.unchanged ? `${run.unchanged} unchanged` : "",
|
|
33
33
|
run.notDownloaded ? `${run.notDownloaded} not downloaded (--download)` : "",
|
|
@@ -172,12 +172,11 @@ const showCommand = (messenger) => annotate(new Command("show"), {
|
|
|
172
172
|
stop.release();
|
|
173
173
|
}
|
|
174
174
|
});
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
readsStdin(annotate(text.command("set"), {
|
|
175
|
+
const textUpdateCommand = (messenger) => {
|
|
176
|
+
const update = readsStdin(annotate(new Command("update"), {
|
|
178
177
|
output: { entity: "attachment_text_change" },
|
|
179
178
|
effects: LOCAL_WRITE,
|
|
180
|
-
examples: [`${messenger.app.command} personal attachments
|
|
179
|
+
examples: [`${messenger.app.command} personal attachments update "Book club" 101 --text-file ./transcript.txt`],
|
|
181
180
|
}), {
|
|
182
181
|
name: "text",
|
|
183
182
|
description: "the text of the file",
|
|
@@ -190,7 +189,7 @@ const textCommand = (messenger) => {
|
|
|
190
189
|
.addOption(ownerOnlyOption("--text-file <path>", "read the text from this file; - or none reads stdin"))
|
|
191
190
|
.action(async function (chat, message) {
|
|
192
191
|
const context = messengerContext(this, messenger);
|
|
193
|
-
refuseLocalWrite(context, messenger.app.command, "attachments.
|
|
192
|
+
refuseLocalWrite(context, messenger.app.command, "attachments.update");
|
|
194
193
|
const { attachment, textFile } = this.opts();
|
|
195
194
|
const given = textFile === undefined || textFile === "-" ? await readAll(context.stdin) : readFileSync(textFile, "utf8");
|
|
196
195
|
const done = await context.withServices((services) => services.attachments.setText({
|
|
@@ -201,7 +200,7 @@ const textCommand = (messenger) => {
|
|
|
201
200
|
}));
|
|
202
201
|
renderAnswer(this, context, itemOf(snakeKeys(done)), () => `${done.ref} #${done.attachment} ${done.chars} characters kept\n`);
|
|
203
202
|
});
|
|
204
|
-
return
|
|
203
|
+
return update;
|
|
205
204
|
};
|
|
206
205
|
/** Files of stored messages: what is in them, for `content:` in a search. Nothing here is sent. */
|
|
207
206
|
export const attachmentsCommand = (messenger) => {
|
|
@@ -211,7 +210,7 @@ export const attachmentsCommand = (messenger) => {
|
|
|
211
210
|
.addCommand(extractCommand(messenger))
|
|
212
211
|
.addCommand(listCommand(messenger))
|
|
213
212
|
.addCommand(showCommand(messenger))
|
|
214
|
-
.addCommand(
|
|
213
|
+
.addCommand(textUpdateCommand(messenger));
|
|
215
214
|
addMediaArchiveCommands(attachments, messenger);
|
|
216
215
|
return attachments;
|
|
217
216
|
};
|
|
@@ -107,7 +107,7 @@ export const syncCommand = (messenger) => {
|
|
|
107
107
|
if (prepare) {
|
|
108
108
|
validateCatchUpBounds(preparation);
|
|
109
109
|
refuseLocalWrite(context, messenger.app.command, "conversations.links");
|
|
110
|
-
refuseLocalWrite(context, messenger.app.command, "conversations.
|
|
110
|
+
refuseLocalWrite(context, messenger.app.command, "conversations.embeddings");
|
|
111
111
|
}
|
|
112
112
|
if (background) {
|
|
113
113
|
const named = chat ?? ALL_CHATS;
|
|
@@ -182,19 +182,19 @@ export const chatShown = (account, card) => {
|
|
|
182
182
|
};
|
|
183
183
|
/** The lowest score `score>=n` asks for; the audit's own floor when the query names none. */
|
|
184
184
|
export const scoreFloor = (ast) => {
|
|
185
|
-
const term = nativeTerms(ast, "chats members
|
|
185
|
+
const term = nativeTerms(ast, "chats members check").get("score");
|
|
186
186
|
if (!term)
|
|
187
187
|
return AUDIT_MIN_SCORE;
|
|
188
188
|
const upper = term.operator === "range" ? (term.upper ?? "*") : term.value;
|
|
189
189
|
if (term.operator !== "range" || upper !== "*")
|
|
190
190
|
queryError("query.unsupported-operator", term.span, "score takes a lower bound: score>=3", {
|
|
191
|
-
command: "chats members
|
|
191
|
+
command: "chats members check",
|
|
192
192
|
guide: LIST_QUERY_GUIDE,
|
|
193
193
|
});
|
|
194
194
|
const floor = Number(term.value);
|
|
195
195
|
return Math.ceil(term.lower_inclusive === false ? floor + 1 : floor);
|
|
196
196
|
};
|
|
197
|
-
export const auditAnswer = (audit) => wholeList("chats members
|
|
197
|
+
export const auditAnswer = (audit) => wholeList("chats members check", audit.items.map(memberAnswer), {
|
|
198
198
|
...(audit.more ? { meta: { truncated: true } } : {}),
|
|
199
199
|
...(audit.completeness && audit.completeness.state !== "complete"
|
|
200
200
|
? {
|
|
@@ -45,16 +45,16 @@ export const membersHistory = async (services, chat, query) => {
|
|
|
45
45
|
});
|
|
46
46
|
};
|
|
47
47
|
export const membersAudit = async (services, chat, query, options) => {
|
|
48
|
-
const minScore = scoreFloor(listQuery(typedQuery(query), AUDIT_QUERY, "chats members
|
|
48
|
+
const minScore = scoreFloor(listQuery(typedQuery(query), AUDIT_QUERY, "chats members check"));
|
|
49
49
|
const audit = await services.chats.audit(chat, { ...options, minScore });
|
|
50
50
|
return { audit, body: auditAnswer(audit) };
|
|
51
51
|
};
|
|
52
52
|
/** The source takes a count but no offset, so page n reads n pages and shows the last. */
|
|
53
53
|
export const inviteLinks = async (services, chat, query, { limit, page }) => {
|
|
54
|
-
const terms = nativeTerms(listQuery(typedQuery(query), LINKS_QUERY, "chats
|
|
54
|
+
const terms = nativeTerms(listQuery(typedQuery(query), LINKS_QUERY, "chats links list"), "chats links list");
|
|
55
55
|
const revoked = terms.get("revoked")?.value.toLowerCase() === "true";
|
|
56
56
|
const found = await services.admin.links(chat, { limit: limit * page, revoked });
|
|
57
|
-
return listOf("chats
|
|
57
|
+
return listOf("chats links list", {
|
|
58
58
|
items: found.items.slice((page - 1) * limit).map(inviteLinkAnswer),
|
|
59
59
|
limit,
|
|
60
60
|
has_more: found.hasMore,
|
|
@@ -121,22 +121,22 @@ export const chatsCommand = (messenger) => {
|
|
|
121
121
|
inspect: "https://example.com/invite/book-club",
|
|
122
122
|
join: "https://example.com/invite/book-club",
|
|
123
123
|
leave: '"Book club"',
|
|
124
|
-
"
|
|
125
|
-
"
|
|
126
|
-
"
|
|
127
|
-
"
|
|
128
|
-
"
|
|
124
|
+
"links create": '"Book club"',
|
|
125
|
+
"links list": '"Book club"',
|
|
126
|
+
"links reset": '"Book club"',
|
|
127
|
+
"links remove": '"Book club" https://example.com/invite/book-club',
|
|
128
|
+
"links show": '"Book club"',
|
|
129
129
|
list: "--limit 10",
|
|
130
130
|
"mark-read": '"Book club" --until-id 101',
|
|
131
131
|
"members add": '"Book club" "Alex Example"',
|
|
132
|
-
"members
|
|
132
|
+
"members check": '"Book club"',
|
|
133
133
|
"members fetch": '"Book club"',
|
|
134
134
|
"members history": '"Book club"',
|
|
135
135
|
"members list": '"Book club"',
|
|
136
136
|
"members remove": '"Book club" "Alex Example"',
|
|
137
137
|
moderate: '"Book club" --dry-run',
|
|
138
|
-
"requests
|
|
139
|
-
"requests
|
|
138
|
+
"requests confirm": '"Book club" "Alex Example"',
|
|
139
|
+
"requests reject": '"Book club" "Alex Example"',
|
|
140
140
|
"requests list": '"Book club"',
|
|
141
141
|
"rules set": '"Book club" links report',
|
|
142
142
|
"rules show": '"Book club"',
|
|
@@ -23,7 +23,7 @@ export const membersCommand = (messenger) => {
|
|
|
23
23
|
const body = await context.withServices((services) => membersList(services, chat, context.settings));
|
|
24
24
|
renderAnswer(this, context, body, lines(({ id, name, username, role }) => [id, name ?? "", username ? `@${username}` : "", role ?? ""].join(" ")));
|
|
25
25
|
}));
|
|
26
|
-
members.addCommand(withQuery(annotate(annotate(new Command("
|
|
26
|
+
members.addCommand(withQuery(annotate(annotate(new Command("check"), { effects: REMOTE_READ, offline: false })
|
|
27
27
|
.summary("members that look like bots, with reasons")
|
|
28
28
|
.description("members that look like bots, each with its reasons — read from the member list and the local store; " +
|
|
29
29
|
"never one request per person, and it removes nobody")
|
|
@@ -69,7 +69,7 @@ export const contactsCommand = (messenger) => {
|
|
|
69
69
|
}));
|
|
70
70
|
renderAnswer(this, context, itemOf(contactAnswer(account, card)));
|
|
71
71
|
}));
|
|
72
|
-
annotate(contacts.command("
|
|
72
|
+
annotate(contacts.command("info"), { effects: REMOTE_READ, output: { entity: "contact_profile" } })
|
|
73
73
|
.summary("a person's profile from the messenger")
|
|
74
74
|
.description("everything the messenger says about one person — handles, flags, last seen, when they registered — and " +
|
|
75
75
|
"how many of their messages the store holds in each chat you share, the first and the last, and the " +
|
|
@@ -173,7 +173,10 @@ export const contactsCommand = (messenger) => {
|
|
|
173
173
|
meta: { ...window.meta, ...(hasMore ? { truncated: true } : {}) },
|
|
174
174
|
}));
|
|
175
175
|
});
|
|
176
|
-
annotate(contacts.command("
|
|
176
|
+
annotate(contacts.command("events"), {
|
|
177
|
+
effects: REMOTE_READ,
|
|
178
|
+
output: { entity: "contact_activity" },
|
|
179
|
+
})
|
|
177
180
|
.summary("one person's activity across their linked identities")
|
|
178
181
|
.description("everything one person took part in, in every messenger linked to them — messages they wrote or were " +
|
|
179
182
|
"mentioned in, chats, mail, meetings, tasks — newest first, from the store; never connects")
|
|
@@ -200,7 +203,7 @@ export const contactsCommand = (messenger) => {
|
|
|
200
203
|
...(cursor === undefined ? {} : { cursor }),
|
|
201
204
|
}));
|
|
202
205
|
const { items, hasMore, limits, pending } = found;
|
|
203
|
-
renderAnswer(this, context, listOf("contacts
|
|
206
|
+
renderAnswer(this, context, listOf("contacts events", {
|
|
204
207
|
items: items.map(snakeKeys),
|
|
205
208
|
limit: limits.items,
|
|
206
209
|
has_more: hasMore,
|
|
@@ -292,8 +295,8 @@ export const contactsCommand = (messenger) => {
|
|
|
292
295
|
contacts.addCommand(command);
|
|
293
296
|
const examples = {
|
|
294
297
|
add: '"Alex Example"',
|
|
295
|
-
"
|
|
296
|
-
"
|
|
298
|
+
"aliases remove": '"Alex Example"',
|
|
299
|
+
"aliases set": '"Alex Example" "Reading partner"',
|
|
297
300
|
block: '"Alex Example"',
|
|
298
301
|
check: '"Alex Example"',
|
|
299
302
|
context: '"Alex Example" --limit 10',
|
|
@@ -306,12 +309,12 @@ export const contactsCommand = (messenger) => {
|
|
|
306
309
|
"notes list": '"Alex Example"',
|
|
307
310
|
"notes remove": '"Alex Example" 1',
|
|
308
311
|
"notes show": '"Alex Example" 1',
|
|
309
|
-
|
|
312
|
+
info: '"Alex Example"',
|
|
310
313
|
remove: '"Alex Example"',
|
|
311
314
|
rename: '"Alex Example" Alex Example',
|
|
312
315
|
show: '"Alex Example"',
|
|
313
316
|
sync: "",
|
|
314
|
-
|
|
317
|
+
events: '"Alex Example" --limit 10',
|
|
315
318
|
unblock: '"Alex Example"',
|
|
316
319
|
unlink: '"Alex Example"',
|
|
317
320
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
|
-
import type { AdminRight, GroupSettingKey, Id, Message, PollVote, PrivacyKey, Provider } from "../../domain/models.js";
|
|
2
|
+
import type { AdminRight, GroupSettingKey, Id, Message, Page, PollVote, PrivacyKey, Provider } from "../../domain/models.js";
|
|
3
3
|
import type { MessengerAdapter } from "../../ports/messenger.js";
|
|
4
4
|
import type { EventSink, Warn } from "../../runs/events.js";
|
|
5
5
|
import { type Asker, type SendGuard } from "../../sends/guard.js";
|
|
@@ -18,6 +18,11 @@ import type { GlobalFlags, ResolveOptions, Settings } from "../settings.js";
|
|
|
18
18
|
*/
|
|
19
19
|
export interface StoredMessages {
|
|
20
20
|
message(chatId: Id, messageId: Id): Promise<Message | undefined>;
|
|
21
|
+
/** Stored messages in one provider thread, oldest first within the newest page. No remote fetch. */
|
|
22
|
+
threadMessages?(chatId: Id, threadId: Id, window: {
|
|
23
|
+
limit: number;
|
|
24
|
+
before?: Id;
|
|
25
|
+
}): Promise<Page<Message>>;
|
|
21
26
|
/** The newest message from someone else in the chat, older than `before` when it is given. */
|
|
22
27
|
newestIncoming(chatId: Id, before?: Id): Promise<Message | undefined>;
|
|
23
28
|
/** The newest message in the chat sent at `at` or earlier; without `at`, the oldest one held. */
|
|
@@ -148,20 +153,22 @@ export interface Messenger extends ServiceMessenger {
|
|
|
148
153
|
stickerFileBytes?: number;
|
|
149
154
|
/** Whether shared folders can be joined; yes when unset. */
|
|
150
155
|
folderJoin?: boolean;
|
|
151
|
-
/** Whether another invite link can be made — `chats
|
|
156
|
+
/** Whether another invite link can be made — `chats links create`; yes when unset. */
|
|
152
157
|
inviteLinkCreate?: boolean;
|
|
153
158
|
/** Whether invite links can be listed; yes when unset. */
|
|
154
159
|
inviteLinkList?: boolean;
|
|
155
160
|
/** Whether one invite link can be revoked; yes when unset. */
|
|
156
161
|
inviteLinkRevoke?: boolean;
|
|
157
|
-
/** Whether one invite link can be changed — `chats
|
|
162
|
+
/** Whether one invite link can be changed — `chats links update`; no when unset. */
|
|
158
163
|
inviteLinkUpdate?: boolean;
|
|
159
|
-
/** Whether every pending join request is answered at once — `chats requests
|
|
164
|
+
/** Whether every pending join request is answered at once — `chats requests confirm --all`; yes when unset. */
|
|
160
165
|
joinRequestsAll?: boolean;
|
|
161
166
|
/** Whether `--all` can take only the requests made by one invite link — `--link`; yes when unset. */
|
|
162
167
|
joinRequestsByLink?: boolean;
|
|
163
168
|
/** Whether a channel post can be commented on — `messages send --comment-to`; yes when unset. */
|
|
164
169
|
channelComments?: boolean;
|
|
170
|
+
/** Whether comments can be read independently of sending; inherits channelComments when unset. */
|
|
171
|
+
readsComments?: boolean;
|
|
165
172
|
/** Whether people added can be shown the messages from before they came — `members add --history`; yes when unset. */
|
|
166
173
|
addsWithHistory?: boolean;
|
|
167
174
|
/** The admin rights this messenger has, as `admins add --can` offers them; every one when unset. */
|
|
@@ -40,6 +40,12 @@ export const storedMessages = (provider, store, account) => {
|
|
|
40
40
|
const at = key();
|
|
41
41
|
return at && (await store()).messages.message(at, messageId, { chatId });
|
|
42
42
|
},
|
|
43
|
+
threadMessages: async (chatId, threadId, window) => {
|
|
44
|
+
const owner = key();
|
|
45
|
+
return owner
|
|
46
|
+
? (await store()).messages.messages(owner, chatId, { ...window, threadId })
|
|
47
|
+
: { items: [], hasMore: false };
|
|
48
|
+
},
|
|
43
49
|
newestIncoming: async (chatId, before) => {
|
|
44
50
|
const at = key();
|
|
45
51
|
if (!at)
|
|
@@ -2,13 +2,14 @@ import { annotate } from "@wirecat/cli-base/commands";
|
|
|
2
2
|
import { commandAt } from "../steps.js";
|
|
3
3
|
export const conversationExamples = (root, tool) => {
|
|
4
4
|
const examples = {
|
|
5
|
-
"batches
|
|
5
|
+
"batches show": '--chat "Book club"',
|
|
6
6
|
"batches status": '--chat "Book club"',
|
|
7
|
-
|
|
7
|
+
"links rebuild": '--chat "Book club"',
|
|
8
8
|
"consents list": "",
|
|
9
|
-
"consents
|
|
10
|
-
|
|
11
|
-
"
|
|
9
|
+
"consents remove": '--chat "Book club"',
|
|
10
|
+
embed: '--chat "Book club"',
|
|
11
|
+
"embeddings clear": '--chat "Book club"',
|
|
12
|
+
"embeddings status": '--chat "Book club"',
|
|
12
13
|
"links add": '--batch "$batch"',
|
|
13
14
|
"links clear": '--chat "Book club"',
|
|
14
15
|
list: '"Book club"',
|
|
@@ -21,5 +22,5 @@ export const conversationExamples = (root, tool) => {
|
|
|
21
22
|
if (command)
|
|
22
23
|
annotate(command, { examples: [`${tool} personal conversations ${path} ${args}`.trimEnd()] });
|
|
23
24
|
}
|
|
24
|
-
commandAt(root, "links add")?.addHelpText("after", "\nSet batch to the id from conversations batches
|
|
25
|
+
commandAt(root, "links add")?.addHelpText("after", "\nSet batch to the id from conversations batches show; supply the JSON answer to that batch on stdin.\n");
|
|
25
26
|
};
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { type Messenger } from "./context.js";
|
|
3
|
-
/**
|
|
4
|
-
* `conversations`: the threads inside a group chat, found by rules over the stored messages — replies,
|
|
5
|
-
* mentions, one sender's messages in a row. From the store alone; nothing is built until asked (phase 3).
|
|
6
|
-
*/
|
|
7
3
|
export declare const conversationsCommand: (messenger: Messenger) => Command;
|
|
8
4
|
/** `messages links`: why a message sits where it does in its conversation. */
|
|
9
5
|
export declare const linksCommand: (messenger: Messenger) => Command;
|