@wirecat/cli-core 0.404.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 +45 -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/first-run.js +3 -3
- 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/messages-send-command.js +44 -3
- 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 +2 -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/setup-answers.d.ts +2 -2
- package/dist/cli/setup-answers.js +2 -2
- 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/index.d.ts +380 -0
- package/dist/entities/index.js +2 -1
- package/dist/entities/member.d.ts +2 -2
- package/dist/entities/member.js +2 -2
- package/dist/entities/message-write.d.ts +386 -0
- package/dist/entities/message-write.js +6 -0
- 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 +8 -1
- package/dist/ports/traits.d.ts +2 -0
- 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.d.ts +1 -1
- package/dist/sends/permissions.js +35 -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/messages.d.ts +5 -0
- package/dist/services/messages.js +23 -0
- 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 +186 -179
- package/skills/follow-ups/SKILL.md +5 -5
- package/skills/link-conversations/SKILL.md +8 -8
|
@@ -38,7 +38,6 @@ export declare const meetingTools: ({
|
|
|
38
38
|
max_read_bytes?: undefined;
|
|
39
39
|
cursor?: undefined;
|
|
40
40
|
} | {
|
|
41
|
-
limit?: undefined;
|
|
42
41
|
event_id?: undefined;
|
|
43
42
|
series_id?: undefined;
|
|
44
43
|
page?: undefined;
|
|
@@ -54,11 +53,11 @@ export declare const meetingTools: ({
|
|
|
54
53
|
format?: undefined;
|
|
55
54
|
transcript_id?: undefined;
|
|
56
55
|
query?: undefined;
|
|
56
|
+
limit?: undefined;
|
|
57
57
|
scan_limit?: undefined;
|
|
58
58
|
max_read_bytes?: undefined;
|
|
59
59
|
cursor?: undefined;
|
|
60
60
|
} | {
|
|
61
|
-
limit?: undefined;
|
|
62
61
|
event_id?: undefined;
|
|
63
62
|
series_id?: undefined;
|
|
64
63
|
page?: undefined;
|
|
@@ -76,11 +75,11 @@ export declare const meetingTools: ({
|
|
|
76
75
|
format?: undefined;
|
|
77
76
|
transcript_id?: undefined;
|
|
78
77
|
query?: undefined;
|
|
78
|
+
limit?: undefined;
|
|
79
79
|
scan_limit?: undefined;
|
|
80
80
|
max_read_bytes?: undefined;
|
|
81
81
|
cursor?: undefined;
|
|
82
82
|
} | {
|
|
83
|
-
limit?: undefined;
|
|
84
83
|
event_id?: undefined;
|
|
85
84
|
series_id?: undefined;
|
|
86
85
|
page?: undefined;
|
|
@@ -102,6 +101,7 @@ export declare const meetingTools: ({
|
|
|
102
101
|
minimum: number;
|
|
103
102
|
};
|
|
104
103
|
query?: undefined;
|
|
104
|
+
limit?: undefined;
|
|
105
105
|
scan_limit?: undefined;
|
|
106
106
|
max_read_bytes?: undefined;
|
|
107
107
|
cursor?: undefined;
|
|
@@ -188,7 +188,6 @@ export declare const meetingTools: ({
|
|
|
188
188
|
max_read_bytes?: undefined;
|
|
189
189
|
cursor?: undefined;
|
|
190
190
|
} | {
|
|
191
|
-
limit?: undefined;
|
|
192
191
|
event_id?: undefined;
|
|
193
192
|
series_id?: undefined;
|
|
194
193
|
page?: undefined;
|
|
@@ -204,11 +203,11 @@ export declare const meetingTools: ({
|
|
|
204
203
|
format?: undefined;
|
|
205
204
|
transcript_id?: undefined;
|
|
206
205
|
query?: undefined;
|
|
206
|
+
limit?: undefined;
|
|
207
207
|
scan_limit?: undefined;
|
|
208
208
|
max_read_bytes?: undefined;
|
|
209
209
|
cursor?: undefined;
|
|
210
210
|
} | {
|
|
211
|
-
limit?: undefined;
|
|
212
211
|
event_id?: undefined;
|
|
213
212
|
series_id?: undefined;
|
|
214
213
|
page?: undefined;
|
|
@@ -226,11 +225,11 @@ export declare const meetingTools: ({
|
|
|
226
225
|
format?: undefined;
|
|
227
226
|
transcript_id?: undefined;
|
|
228
227
|
query?: undefined;
|
|
228
|
+
limit?: undefined;
|
|
229
229
|
scan_limit?: undefined;
|
|
230
230
|
max_read_bytes?: undefined;
|
|
231
231
|
cursor?: undefined;
|
|
232
232
|
} | {
|
|
233
|
-
limit?: undefined;
|
|
234
233
|
event_id?: undefined;
|
|
235
234
|
series_id?: undefined;
|
|
236
235
|
page?: undefined;
|
|
@@ -252,6 +251,7 @@ export declare const meetingTools: ({
|
|
|
252
251
|
minimum: number;
|
|
253
252
|
};
|
|
254
253
|
query?: undefined;
|
|
254
|
+
limit?: undefined;
|
|
255
255
|
scan_limit?: undefined;
|
|
256
256
|
max_read_bytes?: undefined;
|
|
257
257
|
cursor?: undefined;
|
|
@@ -338,7 +338,6 @@ export declare const meetingTools: ({
|
|
|
338
338
|
max_read_bytes?: undefined;
|
|
339
339
|
cursor?: undefined;
|
|
340
340
|
} | {
|
|
341
|
-
limit?: undefined;
|
|
342
341
|
event_id?: undefined;
|
|
343
342
|
series_id?: undefined;
|
|
344
343
|
page?: undefined;
|
|
@@ -354,11 +353,11 @@ export declare const meetingTools: ({
|
|
|
354
353
|
format?: undefined;
|
|
355
354
|
transcript_id?: undefined;
|
|
356
355
|
query?: undefined;
|
|
356
|
+
limit?: undefined;
|
|
357
357
|
scan_limit?: undefined;
|
|
358
358
|
max_read_bytes?: undefined;
|
|
359
359
|
cursor?: undefined;
|
|
360
360
|
} | {
|
|
361
|
-
limit?: undefined;
|
|
362
361
|
event_id?: undefined;
|
|
363
362
|
series_id?: undefined;
|
|
364
363
|
page?: undefined;
|
|
@@ -376,11 +375,11 @@ export declare const meetingTools: ({
|
|
|
376
375
|
format?: undefined;
|
|
377
376
|
transcript_id?: undefined;
|
|
378
377
|
query?: undefined;
|
|
378
|
+
limit?: undefined;
|
|
379
379
|
scan_limit?: undefined;
|
|
380
380
|
max_read_bytes?: undefined;
|
|
381
381
|
cursor?: undefined;
|
|
382
382
|
} | {
|
|
383
|
-
limit?: undefined;
|
|
384
383
|
event_id?: undefined;
|
|
385
384
|
series_id?: undefined;
|
|
386
385
|
page?: undefined;
|
|
@@ -402,6 +401,7 @@ export declare const meetingTools: ({
|
|
|
402
401
|
minimum: number;
|
|
403
402
|
};
|
|
404
403
|
query?: undefined;
|
|
404
|
+
limit?: undefined;
|
|
405
405
|
scan_limit?: undefined;
|
|
406
406
|
max_read_bytes?: undefined;
|
|
407
407
|
cursor?: undefined;
|
|
@@ -488,7 +488,6 @@ export declare const meetingTools: ({
|
|
|
488
488
|
max_read_bytes?: undefined;
|
|
489
489
|
cursor?: undefined;
|
|
490
490
|
} | {
|
|
491
|
-
limit?: undefined;
|
|
492
491
|
event_id?: undefined;
|
|
493
492
|
series_id?: undefined;
|
|
494
493
|
page?: undefined;
|
|
@@ -504,11 +503,11 @@ export declare const meetingTools: ({
|
|
|
504
503
|
format?: undefined;
|
|
505
504
|
transcript_id?: undefined;
|
|
506
505
|
query?: undefined;
|
|
506
|
+
limit?: undefined;
|
|
507
507
|
scan_limit?: undefined;
|
|
508
508
|
max_read_bytes?: undefined;
|
|
509
509
|
cursor?: undefined;
|
|
510
510
|
} | {
|
|
511
|
-
limit?: undefined;
|
|
512
511
|
event_id?: undefined;
|
|
513
512
|
series_id?: undefined;
|
|
514
513
|
page?: undefined;
|
|
@@ -526,11 +525,11 @@ export declare const meetingTools: ({
|
|
|
526
525
|
format?: undefined;
|
|
527
526
|
transcript_id?: undefined;
|
|
528
527
|
query?: undefined;
|
|
528
|
+
limit?: undefined;
|
|
529
529
|
scan_limit?: undefined;
|
|
530
530
|
max_read_bytes?: undefined;
|
|
531
531
|
cursor?: undefined;
|
|
532
532
|
} | {
|
|
533
|
-
limit?: undefined;
|
|
534
533
|
event_id?: undefined;
|
|
535
534
|
series_id?: undefined;
|
|
536
535
|
page?: undefined;
|
|
@@ -552,6 +551,7 @@ export declare const meetingTools: ({
|
|
|
552
551
|
minimum: number;
|
|
553
552
|
};
|
|
554
553
|
query?: undefined;
|
|
554
|
+
limit?: undefined;
|
|
555
555
|
scan_limit?: undefined;
|
|
556
556
|
max_read_bytes?: undefined;
|
|
557
557
|
cursor?: undefined;
|
|
@@ -638,7 +638,6 @@ export declare const meetingTools: ({
|
|
|
638
638
|
max_read_bytes?: undefined;
|
|
639
639
|
cursor?: undefined;
|
|
640
640
|
} | {
|
|
641
|
-
limit?: undefined;
|
|
642
641
|
event_id?: undefined;
|
|
643
642
|
series_id?: undefined;
|
|
644
643
|
page?: undefined;
|
|
@@ -654,11 +653,11 @@ export declare const meetingTools: ({
|
|
|
654
653
|
format?: undefined;
|
|
655
654
|
transcript_id?: undefined;
|
|
656
655
|
query?: undefined;
|
|
656
|
+
limit?: undefined;
|
|
657
657
|
scan_limit?: undefined;
|
|
658
658
|
max_read_bytes?: undefined;
|
|
659
659
|
cursor?: undefined;
|
|
660
660
|
} | {
|
|
661
|
-
limit?: undefined;
|
|
662
661
|
event_id?: undefined;
|
|
663
662
|
series_id?: undefined;
|
|
664
663
|
page?: undefined;
|
|
@@ -676,11 +675,11 @@ export declare const meetingTools: ({
|
|
|
676
675
|
format?: undefined;
|
|
677
676
|
transcript_id?: undefined;
|
|
678
677
|
query?: undefined;
|
|
678
|
+
limit?: undefined;
|
|
679
679
|
scan_limit?: undefined;
|
|
680
680
|
max_read_bytes?: undefined;
|
|
681
681
|
cursor?: undefined;
|
|
682
682
|
} | {
|
|
683
|
-
limit?: undefined;
|
|
684
683
|
event_id?: undefined;
|
|
685
684
|
series_id?: undefined;
|
|
686
685
|
page?: undefined;
|
|
@@ -702,6 +701,7 @@ export declare const meetingTools: ({
|
|
|
702
701
|
minimum: number;
|
|
703
702
|
};
|
|
704
703
|
query?: undefined;
|
|
704
|
+
limit?: undefined;
|
|
705
705
|
scan_limit?: undefined;
|
|
706
706
|
max_read_bytes?: undefined;
|
|
707
707
|
cursor?: undefined;
|
package/dist/ports/app.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export interface AppIdentity {
|
|
|
10
10
|
envPrefix: string;
|
|
11
11
|
description: string;
|
|
12
12
|
version: string;
|
|
13
|
-
/** Where a problem report goes — a new-issue page. `doctor
|
|
13
|
+
/** Where a problem report goes — a new-issue page. `doctor reports create` names it. */
|
|
14
14
|
issues?: string;
|
|
15
15
|
/** The language of the transcript a person reads: its day headings and the word for "you". `ru-RU` when unset. */
|
|
16
16
|
locale?: string;
|
|
@@ -110,6 +110,8 @@ export interface MessengerCore {
|
|
|
110
110
|
chat(chat: string): Promise<ChatCard>;
|
|
111
111
|
/** An option the messenger has no way to honour is refused, never dropped. */
|
|
112
112
|
send(chatId: Id, text: string, options: SendOptions): Promise<Sent>;
|
|
113
|
+
/** Save a nonempty draft for the owner to review and send; never sends a message. */
|
|
114
|
+
saveDraft?(chatId: Id, text: string, options: Pick<SendOptions, "replyTo" | "threadId" | "formatting" | "noPreview">): Promise<void>;
|
|
113
115
|
logout(): Promise<void>;
|
|
114
116
|
close(): Promise<void>;
|
|
115
117
|
}
|
|
@@ -375,6 +377,11 @@ export interface HistoryBatch {
|
|
|
375
377
|
votes?: HistoryVote[];
|
|
376
378
|
reactions?: HistoryReaction[];
|
|
377
379
|
stars?: HistoryStar[];
|
|
380
|
+
/** Deletions learned from a synced snapshot, scoped to their chat and applied after the batch's messages. */
|
|
381
|
+
deletions?: {
|
|
382
|
+
chatId: Id;
|
|
383
|
+
messageId: Id;
|
|
384
|
+
}[];
|
|
378
385
|
events?: HistoryChatEvent[];
|
|
379
386
|
}
|
|
380
387
|
/** For a messenger that pushes its history to the client instead of answering for it (`Messenger.history: "store"`). */
|
|
@@ -486,7 +493,7 @@ export interface AccountTools {
|
|
|
486
493
|
/** The owner's contact list as the messenger keeps it — the address book, not the chats. */
|
|
487
494
|
addressBook(): Promise<Member[]>;
|
|
488
495
|
}
|
|
489
|
-
/** Everything the messenger says about one person — what `contacts
|
|
496
|
+
/** Everything the messenger says about one person — what `contacts info` shows. */
|
|
490
497
|
export interface PersonProfiles {
|
|
491
498
|
/** Reading it tells them nothing. A chat that is not a person is refused. */
|
|
492
499
|
profile(person: string): Promise<ProfileFacts>;
|
package/dist/ports/traits.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ export interface Fetching {
|
|
|
24
24
|
export type MediaOption = "spoiler" | "captionAbove" | "fileName";
|
|
25
25
|
/** What a messenger is and can do, as the services read it; the CLI's `Messenger` adds how its commands run. */
|
|
26
26
|
export interface MessengerTraits {
|
|
27
|
+
/** Opt in to `messages send --draft` only when the adapter implements `saveDraft`. */
|
|
28
|
+
drafts?: boolean;
|
|
27
29
|
counterFields?: readonly CounterField[];
|
|
28
30
|
app: AppIdentity;
|
|
29
31
|
provider: Provider;
|
|
@@ -2,7 +2,7 @@ import { normalize } from "../store/normalize.js";
|
|
|
2
2
|
import { createStemmer, DEFAULT_STEMMERS } from "./stem.js";
|
|
3
3
|
import { amountOf, dayOf, dueDayOf } from "./task-signature.js";
|
|
4
4
|
/**
|
|
5
|
-
* How alike two tasks are, field by field, as the tasks
|
|
5
|
+
* How alike two tasks are, field by field, as the tasks update's duplicate chain scores them
|
|
6
6
|
* (`docs/dev/tasks-plan.md`, "Duplicates — the comparison chain" § 1, 4, 5). Ported from the scorer that
|
|
7
7
|
* tuned the weights, WireCatLabs/cli-testing `performance/tasks/duplicates/` (cli-testing#60); the counts
|
|
8
8
|
* quoted beside each weight are that run's duplicate / other candidate pairs at the level.
|
package/dist/sends/guard.js
CHANGED
|
@@ -12,12 +12,12 @@ const HOUR_MS = 60 * 60 * 1000;
|
|
|
12
12
|
*/
|
|
13
13
|
const READS_UNDER_HOLD = new Set([
|
|
14
14
|
"attachments.extract",
|
|
15
|
-
"conversations.
|
|
15
|
+
"conversations.embeddings",
|
|
16
16
|
"conversations.links",
|
|
17
17
|
"messages.server-search",
|
|
18
18
|
"messages.sync-first",
|
|
19
19
|
"stats.contacts.top.sync-first",
|
|
20
|
-
"stats.messages.counters.
|
|
20
|
+
"stats.messages.counters.sync",
|
|
21
21
|
"stats.messages.show.sync-first",
|
|
22
22
|
"stats.messages.top.sync-first",
|
|
23
23
|
"store.gaps.repair",
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Level } from "./permissions.js";
|
|
2
|
+
/** Canonical names for internal permission keys; saved moved paths are refused below. */
|
|
3
|
+
export declare const canonicalPermissionKey: (key: string) => string;
|
|
4
|
+
/** WC-PERM-8: a moved saved path must name the replacement before any command runs. */
|
|
5
|
+
export declare const canonicalPermissions: (levels: Readonly<Record<string, Level>>) => Record<string, Level>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
const RENAMED = [
|
|
3
|
+
["models.audio.download", "models.download"],
|
|
4
|
+
["models.text.download", "models.download"],
|
|
5
|
+
["models.text.key", "models.keys"],
|
|
6
|
+
["models.audio", "models"],
|
|
7
|
+
["models.text", "models"],
|
|
8
|
+
["doctor.report", "doctor.reports"],
|
|
9
|
+
["contacts.aliases.rm", "contacts.aliases.remove"],
|
|
10
|
+
["contacts.alias.rm", "contacts.aliases.remove"],
|
|
11
|
+
["contacts.alias", "contacts.aliases"],
|
|
12
|
+
["contacts.profile", "contacts.info"],
|
|
13
|
+
["contacts.timeline", "contacts.events"],
|
|
14
|
+
["chats.links.revoke", "chats.links.remove"],
|
|
15
|
+
["chats.link.revoke", "chats.links.remove"],
|
|
16
|
+
["chats.link", "chats.links"],
|
|
17
|
+
["chats.members.audit", "chats.members.check"],
|
|
18
|
+
["chats.requests.accept", "chats.requests.confirm"],
|
|
19
|
+
["chats.requests.decline", "chats.requests.reject"],
|
|
20
|
+
["metadata.get", "metadata.show"],
|
|
21
|
+
["metadata.refresh", "metadata.sync"],
|
|
22
|
+
["tasks.plan", "tasks.update"],
|
|
23
|
+
["tags.auto", "tags.rebuild"],
|
|
24
|
+
["attachments.text.set", "attachments.update"],
|
|
25
|
+
["attachments.text", "attachments.update"],
|
|
26
|
+
["conversations.build", "conversations.links.rebuild"],
|
|
27
|
+
["conversations.embed", "conversations.embeddings"],
|
|
28
|
+
["conversations.batches.next", "conversations.batches.show"],
|
|
29
|
+
["conversations.consents.revoke", "conversations.consents.remove"],
|
|
30
|
+
["stats.messages.counters.refresh", "stats.messages.counters.sync"],
|
|
31
|
+
["store.gaps.plan", "store.gaps.show"],
|
|
32
|
+
["store.reindex", "store.rebuild"],
|
|
33
|
+
];
|
|
34
|
+
/** Canonical names for internal permission keys; saved moved paths are refused below. */
|
|
35
|
+
export const canonicalPermissionKey = (key) => {
|
|
36
|
+
if (key.startsWith("bot."))
|
|
37
|
+
return `bot.${canonicalPermissionKey(key.slice(4))}`;
|
|
38
|
+
const renamed = RENAMED.find(([old]) => key === old || key.startsWith(`${old}.`));
|
|
39
|
+
return renamed ? renamed[1] + key.slice(renamed[0].length) : key;
|
|
40
|
+
};
|
|
41
|
+
/** WC-PERM-8: a moved saved path must name the replacement before any command runs. */
|
|
42
|
+
export const canonicalPermissions = (levels) => {
|
|
43
|
+
for (const key of Object.keys(levels)) {
|
|
44
|
+
const canonical = canonicalPermissionKey(key);
|
|
45
|
+
if (key !== canonical)
|
|
46
|
+
throw new CliError("configuration_error", `permission path moved — rename permissions.${key} to permissions.${canonical}`, { permission: key, replacement: canonical, retryable: false });
|
|
47
|
+
}
|
|
48
|
+
return { ...levels };
|
|
49
|
+
};
|
|
@@ -31,7 +31,7 @@ export declare const isBotApi: (path: readonly string[]) => boolean;
|
|
|
31
31
|
*/
|
|
32
32
|
export declare const botApiAllowed: (levels: Readonly<Record<PermissionKey, Level>>) => boolean;
|
|
33
33
|
/** The resources at the top of the command tree, which `readOnly` and `allow` turn read-only as a whole. */
|
|
34
|
-
export declare const RESOURCES: readonly ["messages", "reactions", "polls", "topics", "chats", "contacts", "account", "bot", "conversations", "tags", "mentions", "search", "searches", "tasks", "replies", "attachments", "stats", "store", "metadata", "mcp", "server", "models", "schedule", "doctor", "images"];
|
|
34
|
+
export declare const RESOURCES: readonly ["messages", "drafts", "reactions", "polls", "topics", "chats", "contacts", "account", "bot", "conversations", "tags", "mentions", "search", "searches", "tasks", "replies", "attachments", "stats", "store", "metadata", "mcp", "server", "models", "schedule", "doctor", "images"];
|
|
35
35
|
/** The command path an old `allow` word stood for, as one key — what an MCP tool's `permission` is checked against. */
|
|
36
36
|
export declare const keyOfWord: (word: Permission) => PermissionKey;
|
|
37
37
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
import { canonicalPermissionKey, canonicalPermissions } from "./permission-aliases.js";
|
|
2
3
|
/**
|
|
3
4
|
* What a profile may be allowed to do (max-cli `CLI-37`, `NEED-251`). One name per thing the owner would
|
|
4
5
|
* recognise, so `sessions` — which logs the owner out of their phone — never rides along with
|
|
@@ -91,12 +92,11 @@ export const DEFAULT_PERMISSIONS = {
|
|
|
91
92
|
"server.start": "ask",
|
|
92
93
|
"server.stop": "ask",
|
|
93
94
|
"server.restart": "ask",
|
|
94
|
-
"models.
|
|
95
|
-
"models.text.download": "ask",
|
|
95
|
+
"models.download": "ask",
|
|
96
96
|
"images.models.download": "ask",
|
|
97
97
|
"schedule.run": "ask",
|
|
98
98
|
"schedule.exec": "ask",
|
|
99
|
-
"doctor.
|
|
99
|
+
"doctor.reports.create": "ask",
|
|
100
100
|
};
|
|
101
101
|
/** Commands under a housekeeping word, or store maintenance, that still have a key of their own: their path. */
|
|
102
102
|
const KEYED_UNDER_HOUSEKEEPING = new Set(Object.keys(DEFAULT_PERMISSIONS).filter((key) => /^(store|server|models|images|schedule|doctor)\./.test(key)));
|
|
@@ -122,6 +122,7 @@ export const botApiAllowed = (levels) => {
|
|
|
122
122
|
/** The resources at the top of the command tree, which `readOnly` and `allow` turn read-only as a whole. */
|
|
123
123
|
export const RESOURCES = [
|
|
124
124
|
"messages",
|
|
125
|
+
"drafts",
|
|
125
126
|
"reactions",
|
|
126
127
|
"polls",
|
|
127
128
|
"topics",
|
|
@@ -162,7 +163,7 @@ const OLD_WORDS = {
|
|
|
162
163
|
"chats.leave",
|
|
163
164
|
"chats.members",
|
|
164
165
|
"chats.admins",
|
|
165
|
-
"chats.
|
|
166
|
+
"chats.links",
|
|
166
167
|
"chats.requests",
|
|
167
168
|
"chats.moderate",
|
|
168
169
|
"topics.enable",
|
|
@@ -198,7 +199,7 @@ export const layerPermissions = (layers) => {
|
|
|
198
199
|
const sources = {};
|
|
199
200
|
for (const [from, layer] of layers) {
|
|
200
201
|
const nearer = Object.keys(levels);
|
|
201
|
-
for (const [key, level] of Object.entries(layer ?? {})) {
|
|
202
|
+
for (const [key, level] of Object.entries(canonicalPermissions(layer ?? {}))) {
|
|
202
203
|
if (nearer.some((named) => key === named || key.startsWith(`${named}.`)))
|
|
203
204
|
continue;
|
|
204
205
|
levels[key] = level;
|
|
@@ -235,6 +236,8 @@ const FORMER_KEYS = { "chats.unmute": "chats.mute" };
|
|
|
235
236
|
* A path nothing names is allowed.
|
|
236
237
|
*/
|
|
237
238
|
export const levelFor = (permissions, key, defaults = DEFAULT_PERMISSIONS) => {
|
|
239
|
+
permissions = canonicalPermissions(permissions);
|
|
240
|
+
key = canonicalPermissionKey(key);
|
|
238
241
|
const found = profileLevel(permissions, key, defaults);
|
|
239
242
|
const ceiling = permissions[CEILING_KEY];
|
|
240
243
|
return ceiling && ORDER[ceiling] < ORDER[found.level] ? { level: ceiling, key: CEILING_KEY } : found;
|
|
@@ -289,7 +292,7 @@ const KIND_KEYS = {
|
|
|
289
292
|
/** The command path a guarded write belongs to, when the caller did not name it. */
|
|
290
293
|
export const keyForWrite = (kind, action) => {
|
|
291
294
|
if (kind === "chat")
|
|
292
|
-
return CHAT_KEYS[action ?? ""] ?? `chats.${action ?? "update"}
|
|
295
|
+
return canonicalPermissionKey(CHAT_KEYS[action ?? ""] ?? `chats.${action ?? "update"}`);
|
|
293
296
|
if (kind === "account") {
|
|
294
297
|
const key = ACCOUNT_KEYS[action];
|
|
295
298
|
if (!key)
|
|
@@ -300,6 +303,7 @@ export const keyForWrite = (kind, action) => {
|
|
|
300
303
|
};
|
|
301
304
|
/** `keyForWrite` read backwards, as far as a command's key says it: an unpin is a `pin`, a chat change names its action. */
|
|
302
305
|
export const writeOfKey = (key) => {
|
|
306
|
+
key = canonicalPermissionKey(key);
|
|
303
307
|
const account = Object.entries(ACCOUNT_KEYS).find(([, named]) => named === key)?.[0];
|
|
304
308
|
if (account)
|
|
305
309
|
return { kind: "account", action: account };
|
|
@@ -316,20 +320,21 @@ export const writeOfKey = (key) => {
|
|
|
316
320
|
};
|
|
317
321
|
/** Write keys written out where a write is checked; `permission-keys.test.ts` keeps this list whole. */
|
|
318
322
|
const NAMED_WRITE_KEYS = [
|
|
323
|
+
"drafts",
|
|
319
324
|
"store.gaps.repair",
|
|
320
325
|
"messages.sync-first",
|
|
321
326
|
"messages.server-search",
|
|
322
327
|
"messages.press",
|
|
323
328
|
"chats.start",
|
|
324
329
|
"chats.app",
|
|
325
|
-
"stats.messages.counters.
|
|
330
|
+
"stats.messages.counters.sync",
|
|
326
331
|
"stats.messages.show.sync-first",
|
|
327
332
|
"stats.messages.top.sync-first",
|
|
328
333
|
"stats.contacts.top.sync-first",
|
|
329
334
|
"account.sessions.list",
|
|
330
335
|
"attachments.extract",
|
|
331
336
|
"attachments.move",
|
|
332
|
-
"attachments.
|
|
337
|
+
"attachments.update",
|
|
333
338
|
"bot.callbacks.answer",
|
|
334
339
|
"bot.chats.action",
|
|
335
340
|
"bot.chats.admins.add",
|
|
@@ -344,14 +349,14 @@ const NAMED_WRITE_KEYS = [
|
|
|
344
349
|
"bot.messages.unpin",
|
|
345
350
|
"chats.admins.add",
|
|
346
351
|
"chats.admins.remove",
|
|
347
|
-
"chats.
|
|
348
|
-
"chats.
|
|
349
|
-
"chats.
|
|
350
|
-
"chats.
|
|
352
|
+
"chats.links.create",
|
|
353
|
+
"chats.links.reset",
|
|
354
|
+
"chats.links.remove",
|
|
355
|
+
"chats.links.update",
|
|
351
356
|
"chats.members.add",
|
|
352
357
|
"chats.members.remove",
|
|
353
358
|
"chats.moderate",
|
|
354
|
-
"conversations.
|
|
359
|
+
"conversations.embeddings",
|
|
355
360
|
"conversations.links",
|
|
356
361
|
"polls.close",
|
|
357
362
|
"polls.create",
|
|
@@ -359,13 +364,13 @@ const NAMED_WRITE_KEYS = [
|
|
|
359
364
|
"searches.clear",
|
|
360
365
|
"searches.create",
|
|
361
366
|
"searches.delete",
|
|
362
|
-
"contacts.
|
|
363
|
-
"contacts.
|
|
367
|
+
"contacts.aliases.set",
|
|
368
|
+
"contacts.aliases.remove",
|
|
364
369
|
"contacts.notes.add",
|
|
365
370
|
"contacts.notes.edit",
|
|
366
371
|
"contacts.notes.remove",
|
|
367
|
-
"tags.
|
|
368
|
-
"metadata.
|
|
372
|
+
"tags.rebuild",
|
|
373
|
+
"metadata.sync",
|
|
369
374
|
"mentions.rebuild",
|
|
370
375
|
"mentions.confirm",
|
|
371
376
|
"mentions.reject",
|
|
@@ -378,7 +383,7 @@ const NAMED_WRITE_KEYS = [
|
|
|
378
383
|
"tasks.duplicates.reject",
|
|
379
384
|
"tasks.link",
|
|
380
385
|
"tasks.merge",
|
|
381
|
-
"tasks.
|
|
386
|
+
"tasks.update",
|
|
382
387
|
"tasks.release",
|
|
383
388
|
"tasks.scan",
|
|
384
389
|
"tasks.unmerge",
|
|
@@ -395,7 +400,7 @@ export const WRITE_KEYS = [
|
|
|
395
400
|
...Object.values(ACCOUNT_KEYS),
|
|
396
401
|
...Object.values(CHAT_KEYS),
|
|
397
402
|
...PERMISSIONS.flatMap((word) => [...OLD_WORDS[word], ...botKeysOf(word)]),
|
|
398
|
-
]),
|
|
403
|
+
].map(canonicalPermissionKey)),
|
|
399
404
|
];
|
|
400
405
|
/** Commands that look after the tool and its files and never show a message: no level stops them. */
|
|
401
406
|
const HOUSEKEEPING = new Set([
|
|
@@ -481,6 +486,9 @@ export const readKeysForCommand = (path) => {
|
|
|
481
486
|
* `undefined` for a path this package does not know — a CLI's own command, which its CLI maps.
|
|
482
487
|
*/
|
|
483
488
|
export const keyForCommand = (path) => {
|
|
489
|
+
if (path.length === 0)
|
|
490
|
+
return null;
|
|
491
|
+
path = canonicalPermissionKey(path.join(".")).split(".");
|
|
484
492
|
const [top, next] = path;
|
|
485
493
|
if (top === "bot") {
|
|
486
494
|
if (next === undefined || BOT_HOUSEKEEPING.has(next))
|
|
@@ -503,11 +511,12 @@ export const keyForCommand = (path) => {
|
|
|
503
511
|
return null;
|
|
504
512
|
// The agent's answers write only to the local store: their own key, so read-only messages can still link (phase 4 A10).
|
|
505
513
|
if (top === "conversations" && next === "links")
|
|
506
|
-
return "conversations.links";
|
|
514
|
+
return path[2] === "rebuild" ? "conversations.links.rebuild" : "conversations.links";
|
|
507
515
|
// Vectors are written to the local store only, and the text never leaves the machine (phase 5 E10).
|
|
508
|
-
if ((
|
|
516
|
+
if ((top === "conversations" && next === "embeddings") ||
|
|
517
|
+
(top === "images" && next === "embed") ||
|
|
509
518
|
(top === "conversations" && next === "rebuild"))
|
|
510
|
-
return "conversations.
|
|
519
|
+
return "conversations.embeddings";
|
|
511
520
|
// Which files a message has, and where they are saved, tells as much as a message does; so does who
|
|
512
521
|
// shared which document link, and when.
|
|
513
522
|
if ((top === "attachments" && next === "list") || (top === "chats" && next === "documents"))
|
|
@@ -519,8 +528,7 @@ export const keyForCommand = (path) => {
|
|
|
519
528
|
return path.join(".");
|
|
520
529
|
if (top === "messages" && next === "sync")
|
|
521
530
|
return "messages";
|
|
522
|
-
if (SHOW_MESSAGES.has(top) ||
|
|
523
|
-
(top === "contacts" && (next === "context" || next === "prepare" || next === "timeline")))
|
|
531
|
+
if (SHOW_MESSAGES.has(top) || (top === "contacts" && (next === "context" || next === "prepare" || next === "events")))
|
|
524
532
|
return "messages";
|
|
525
533
|
if (RESOURCES.includes(top))
|
|
526
534
|
return path.join(".");
|
|
@@ -536,10 +544,12 @@ export const permissionOverrides = (entries = []) => {
|
|
|
536
544
|
throw new CliError("validation_error", "--permission takes a command path and level, e.g. messages.send=allow (deny, readonly, ask, allow)");
|
|
537
545
|
levels[key] = level;
|
|
538
546
|
}
|
|
539
|
-
return levels;
|
|
547
|
+
return canonicalPermissions(levels);
|
|
540
548
|
};
|
|
541
549
|
/** No startup flag grants access: an override may narrow the profile's level, never lift it or a key under it. */
|
|
542
550
|
export const assertOverridesTighten = (overrides, profile) => {
|
|
551
|
+
overrides = canonicalPermissions(overrides);
|
|
552
|
+
profile = canonicalPermissions(profile);
|
|
543
553
|
for (const [key, level] of Object.entries(overrides)) {
|
|
544
554
|
const under = Object.keys(profile).filter((named) => named.startsWith(`${key}.`));
|
|
545
555
|
const lifted = [key, ...under].find((named) => ORDER[level] > ORDER[levelFor(profile, named).level]);
|
package/dist/services/admin.js
CHANGED
|
@@ -111,14 +111,14 @@ export const adminService = (deps) => {
|
|
|
111
111
|
return { operationId, chat: card };
|
|
112
112
|
},
|
|
113
113
|
link: async (chat) => {
|
|
114
|
-
const group = await capability(await online("chats
|
|
114
|
+
const group = await capability(await online("chats links show"), "group", "read a group")(chat);
|
|
115
115
|
if (group.link === null) {
|
|
116
116
|
throw new CliError("not_found", `${group.title ?? group.id} shows you no invite link — only admins, or members when the group allows it`);
|
|
117
117
|
}
|
|
118
118
|
return { chatId: group.id, title: group.title, link: group.link };
|
|
119
119
|
},
|
|
120
120
|
resetLink: async (chat) => {
|
|
121
|
-
const connection = await online("chats
|
|
121
|
+
const connection = await online("chats links reset");
|
|
122
122
|
const reset = capability(connection, "resetInviteLink", "replace an invite link");
|
|
123
123
|
const { id: chatId } = await connection.resolve(chat);
|
|
124
124
|
const operationId = newOperationId();
|
|
@@ -127,7 +127,7 @@ export const adminService = (deps) => {
|
|
|
127
127
|
},
|
|
128
128
|
createLink: async (chat, { approval, expires, maxUses }) => {
|
|
129
129
|
const expiresAt = linkLimits({ approval, expires, maxUses });
|
|
130
|
-
const connection = await online("chats
|
|
130
|
+
const connection = await online("chats links create");
|
|
131
131
|
const create = capability(connection, "createInviteLink", "make another invite link");
|
|
132
132
|
const { id: chatId } = await connection.resolve(chat);
|
|
133
133
|
const operationId = newOperationId();
|
|
@@ -193,8 +193,8 @@ export const adminService = (deps) => {
|
|
|
193
193
|
},
|
|
194
194
|
revokeLink: async (chat, link) => {
|
|
195
195
|
if (link.trim() === "")
|
|
196
|
-
throw new CliError("validation_error", "which link? give it as `chats
|
|
197
|
-
const connection = await online("chats
|
|
196
|
+
throw new CliError("validation_error", "which link? give it as `chats links list` shows it");
|
|
197
|
+
const connection = await online("chats links remove");
|
|
198
198
|
const revoke = capability(connection, "revokeInviteLink", "revoke an invite link");
|
|
199
199
|
const { id: chatId } = await connection.resolve(chat);
|
|
200
200
|
const operationId = newOperationId();
|
|
@@ -203,12 +203,12 @@ export const adminService = (deps) => {
|
|
|
203
203
|
},
|
|
204
204
|
updateLink: async (chat, link, { approval, expires, maxUses }) => {
|
|
205
205
|
if (link.trim() === "")
|
|
206
|
-
throw new CliError("validation_error", "which link? give it as `chats
|
|
206
|
+
throw new CliError("validation_error", "which link? give it as `chats links list` shows it");
|
|
207
207
|
if (approval === undefined && expires === undefined && maxUses === undefined)
|
|
208
208
|
throw new CliError("validation_error", "nothing to change: give --approval, --no-approval, --expire-time or --max-uses");
|
|
209
209
|
const limited = linkLimits({ approval, expires: expires === "never" ? undefined : expires, maxUses });
|
|
210
210
|
const expiresAt = expires === "never" ? null : limited;
|
|
211
|
-
const connection = await online("chats
|
|
211
|
+
const connection = await online("chats links update");
|
|
212
212
|
const update = capability(connection, "updateInviteLink", "change an invite link");
|
|
213
213
|
const { id: chatId } = await connection.resolve(chat);
|
|
214
214
|
const change = {
|
package/dist/services/chats.js
CHANGED
|
@@ -161,7 +161,7 @@ export const chatsService = (deps) => ({
|
|
|
161
161
|
},
|
|
162
162
|
audit: async (chat, { budget = AUDIT_BUDGET, minScore = AUDIT_MIN_SCORE, pauseMs = AUDIT_PAUSE_MS, deep = 0, registry }) => {
|
|
163
163
|
if (deps.offline) {
|
|
164
|
-
throw new CliError("validation_error", "`chats members
|
|
164
|
+
throw new CliError("validation_error", "`chats members check` reads the member list from the messenger; not with --offline");
|
|
165
165
|
}
|
|
166
166
|
const connection = await deps.connection();
|
|
167
167
|
const { chatId: id, members: read, more, participants } = await readMembers(connection, chat, budget, pauseMs);
|
|
@@ -61,7 +61,7 @@ export interface ConversationsService {
|
|
|
61
61
|
batchStatus(chat: string, size: number): Promise<BatchStatus>;
|
|
62
62
|
/** The next window for the agent; `undefined` when every message is answered (A1–A4). */
|
|
63
63
|
nextBatch(chat: string, size: number): Promise<LinkBatch | undefined>;
|
|
64
|
-
/** Stores the agent's answer to a batch, all or nothing (A5); `conversations
|
|
64
|
+
/** Stores the agent's answer to a batch, all or nothing (A5); `conversations links rebuild` then uses it. */
|
|
65
65
|
addAnswers(batch: string, answer: AgentAnswer): Promise<{
|
|
66
66
|
chat: Id;
|
|
67
67
|
stored: number;
|
|
@@ -39,7 +39,7 @@ export const conversationsService = (deps) => {
|
|
|
39
39
|
const store = await accountStore(deps);
|
|
40
40
|
return { store, chatId: await chatIdIn(deps.messenger, chat, store, { includeLeft: true }) };
|
|
41
41
|
};
|
|
42
|
-
const notBuilt = (chatId) => new CliError("not_found", `chat ${chatId} has no conversations yet — \`${typedCommand(deps.messenger.app.command, `conversations
|
|
42
|
+
const notBuilt = (chatId) => new CliError("not_found", `chat ${chatId} has no conversations yet — \`${typedCommand(deps.messenger.app.command, `conversations links rebuild --chat ${chatId}`)}\``);
|
|
43
43
|
return {
|
|
44
44
|
build: async (chat, { maxMessages, check } = {}) => {
|
|
45
45
|
check?.();
|
|
@@ -115,7 +115,7 @@ export const countersService = (deps) => {
|
|
|
115
115
|
return preview;
|
|
116
116
|
if (deps.offline)
|
|
117
117
|
throw new CliError("validation_error", "counter refresh requires an online session");
|
|
118
|
-
const request = { chatId: null, key: "stats.messages.counters.
|
|
118
|
+
const request = { chatId: null, key: "stats.messages.counters.sync" };
|
|
119
119
|
await deps.guard.ask?.(request);
|
|
120
120
|
deps.guard.check(request, { reserve: false });
|
|
121
121
|
const controller = new AbortController(), timer = setTimeout(() => controller.abort(new Error("counter refresh time budget exhausted")), time);
|
|
@@ -17,11 +17,11 @@ const PER_SESSION = 8;
|
|
|
17
17
|
const PER_REQUEST = 256;
|
|
18
18
|
/** e5's tokenizer reads ~3.7 characters a token in Russian; 3 keeps the bound above the real count. */
|
|
19
19
|
const CHARS_PER_TOKEN_AT_LEAST = 3;
|
|
20
|
-
const notDownloaded = (deps, id) => new CliError("not_found", `${id} is not downloaded — \`${typedCommand(deps.messenger.app.command, `models
|
|
20
|
+
const notDownloaded = (deps, id) => new CliError("not_found", `${id} is not downloaded — \`${typedCommand(deps.messenger.app.command, `models download ${id}`)}\``, {
|
|
21
21
|
reason: "model.not-downloaded",
|
|
22
22
|
model: id,
|
|
23
23
|
});
|
|
24
|
-
export const notBuiltError = (deps, chatId) => new CliError("not_found", `chat ${chatId} has no conversations yet — \`${typedCommand(deps.messenger.app.command, `conversations
|
|
24
|
+
export const notBuiltError = (deps, chatId) => new CliError("not_found", `chat ${chatId} has no conversations yet — \`${typedCommand(deps.messenger.app.command, `conversations links rebuild --chat ${chatId}`)}\``);
|
|
25
25
|
export const embeddingTarget = (deps, choice = DEFAULT_TEXT_MODEL) => {
|
|
26
26
|
if (typeof choice !== "string") {
|
|
27
27
|
const { remote, apiKey, concurrency = 4 } = choice;
|