@wirecat/cli-core 0.399.0 → 0.401.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 +111 -0
- package/README.md +1 -1
- package/dist/analysis/consents.d.ts +11 -6
- package/dist/analysis/consents.js +5 -2
- package/dist/cli/bot/mcp.js +2 -1
- package/dist/cli/bot/store.js +1 -1
- package/dist/cli/config-command.js +20 -1
- package/dist/cli/context.d.ts +2 -0
- package/dist/cli/context.js +1 -1
- package/dist/cli/effects.d.ts +4 -1
- package/dist/cli/effects.js +10 -2
- package/dist/cli/index.d.ts +3 -2
- package/dist/cli/index.js +3 -2
- package/dist/cli/json-fields.js +12 -1
- package/dist/cli/meeting-evidence-answer.d.ts +46 -0
- package/dist/cli/meeting-evidence-answer.js +22 -0
- package/dist/cli/meeting-preparation-answer.js +1 -13
- package/dist/cli/messenger/account-command.js +15 -6
- package/dist/cli/messenger/account-profile-query.d.ts +2 -0
- package/dist/cli/messenger/account-profile-query.js +13 -0
- package/dist/cli/messenger/account-sessions-command.js +1 -2
- package/dist/cli/messenger/admin-commands.js +12 -12
- package/dist/cli/messenger/admin-contacts-command.js +6 -3
- package/dist/cli/messenger/admin-folders-command.js +5 -5
- package/dist/cli/messenger/admin-moderation-command.js +4 -4
- package/dist/cli/messenger/admin-statistics-command.js +6 -14
- package/dist/cli/messenger/analysis-consents-query.d.ts +5 -0
- package/dist/cli/messenger/analysis-consents-query.js +20 -0
- package/dist/cli/messenger/archive-commands.js +3 -3
- package/dist/cli/messenger/attachments-command.js +3 -0
- package/dist/cli/messenger/backfill-command.js +5 -4
- package/dist/cli/messenger/chat-queries.d.ts +1 -0
- package/dist/cli/messenger/chat-queries.js +12 -2
- package/dist/cli/messenger/chats-command.js +2 -2
- package/dist/cli/messenger/chats-documents-command.js +7 -5
- package/dist/cli/messenger/chats-members-command.js +7 -3
- package/dist/cli/messenger/chats-read-command.js +1 -1
- package/dist/cli/messenger/chats-tracking-command.js +14 -6
- package/dist/cli/messenger/contacts-command.js +9 -5
- package/dist/cli/messenger/conversations-command.js +20 -16
- package/dist/cli/messenger/doctor-command.js +4 -1
- package/dist/cli/messenger/document-links-query.d.ts +2 -0
- package/dist/cli/messenger/document-links-query.js +9 -0
- package/dist/cli/messenger/download-command.js +1 -0
- package/dist/cli/messenger/flood-command.d.ts +2 -2
- package/dist/cli/messenger/flood-command.js +3 -3
- package/dist/cli/messenger/gaps-command.js +2 -2
- package/dist/cli/messenger/guard-commands.js +35 -13
- package/dist/cli/messenger/images-command.js +1 -0
- package/dist/cli/messenger/media-archive-command.js +2 -0
- package/dist/cli/messenger/media-archive-move-command.js +1 -0
- package/dist/cli/messenger/media-archive-timer-command.js +1 -1
- package/dist/cli/messenger/mentions-command.js +15 -2
- package/dist/cli/messenger/message-writes.d.ts +192 -2
- package/dist/cli/messenger/message-writes.js +6 -2
- package/dist/cli/messenger/messages-command.js +2 -0
- package/dist/cli/messenger/messages-delete-command.js +1 -0
- package/dist/cli/messenger/messages-edit-command.js +1 -0
- package/dist/cli/messenger/messages-evidence-command.js +3 -1
- package/dist/cli/messenger/messages-forward-command.js +1 -0
- package/dist/cli/messenger/messages-pin-command.js +3 -0
- package/dist/cli/messenger/messages-send-command.js +10 -6
- package/dist/cli/messenger/metadata-command.js +6 -1
- package/dist/cli/messenger/models-command.js +10 -5
- package/dist/cli/messenger/polls-command.js +4 -0
- package/dist/cli/messenger/private-people-command.js +3 -3
- package/dist/cli/messenger/rankings-command.js +6 -11
- package/dist/cli/messenger/reactions-command.js +2 -0
- package/dist/cli/messenger/recipients-query.d.ts +5 -0
- package/dist/cli/messenger/recipients-query.js +18 -0
- package/dist/cli/messenger/records-command.js +13 -8
- package/dist/cli/messenger/review.js +1 -1
- package/dist/cli/messenger/schedule-command.js +2 -2
- package/dist/cli/messenger/search-answers.js +2 -0
- package/dist/cli/messenger/search-command.js +3 -1
- package/dist/cli/messenger/searches-command.js +5 -1
- package/dist/cli/messenger/sends-query.d.ts +5 -0
- package/dist/cli/messenger/sends-query.js +33 -0
- package/dist/cli/messenger/serve-command.js +1 -0
- package/dist/cli/messenger/server-command.js +0 -3
- package/dist/cli/messenger/statistics-query.d.ts +14 -0
- package/dist/cli/messenger/statistics-query.js +143 -0
- package/dist/cli/messenger/stats-command.js +5 -5
- package/dist/cli/messenger/store-maintenance-command.js +10 -10
- package/dist/cli/messenger/stored.js +4 -1
- package/dist/cli/messenger/tags-command.js +7 -1
- package/dist/cli/messenger/tasks-command.js +57 -14
- package/dist/cli/messenger/transcribe-command.js +1 -0
- package/dist/cli/messenger/watch-answers.d.ts +6 -0
- package/dist/cli/messenger/watch-answers.js +69 -0
- package/dist/cli/messenger/watch-command.d.ts +1 -1
- package/dist/cli/messenger/watch-command.js +56 -7
- package/dist/cli/program.js +5 -1
- package/dist/cli/runs/command.js +25 -38
- package/dist/cli/runs/query.d.ts +5 -0
- package/dist/cli/runs/query.js +32 -0
- package/dist/cli/runs/search-command.d.ts +4 -0
- package/dist/cli/runs/search-command.js +27 -0
- package/dist/cli/runs/search-query.d.ts +4 -0
- package/dist/cli/runs/search-query.js +50 -0
- package/dist/cli/runs/search.d.ts +2 -1
- package/dist/cli/runs/search.js +6 -6
- package/dist/documents/index.d.ts +1 -0
- package/dist/documents/index.js +1 -0
- package/dist/domain/attachments.js +9 -1
- package/dist/domain/rankings-options.js +3 -3
- package/dist/entities/account-change.d.ts +89 -0
- package/dist/entities/account-change.js +20 -0
- package/dist/entities/account-profile.d.ts +7 -0
- package/dist/entities/account-profile.js +8 -0
- package/dist/entities/analysis-consent.d.ts +9 -0
- package/dist/entities/analysis-consent.js +4 -0
- package/dist/entities/attachment-results.d.ts +96 -0
- package/dist/entities/attachment-results.js +31 -0
- package/dist/entities/batch-result.d.ts +27 -0
- package/dist/entities/batch-result.js +18 -0
- package/dist/entities/chat-admin.d.ts +234 -0
- package/dist/entities/chat-admin.js +84 -0
- package/dist/entities/chat-change.d.ts +25 -0
- package/dist/entities/chat-change.js +11 -0
- package/dist/entities/chat-metadata.d.ts +34 -0
- package/dist/entities/chat-metadata.js +15 -0
- package/dist/entities/chat-moderation.d.ts +44 -0
- package/dist/entities/chat-moderation.js +37 -0
- package/dist/entities/chat-tracking.d.ts +26 -0
- package/dist/entities/chat-tracking.js +23 -0
- package/dist/entities/contact-activity.d.ts +52 -0
- package/dist/entities/contact-activity.js +42 -0
- package/dist/entities/contact-context.d.ts +1568 -0
- package/dist/entities/contact-context.js +191 -0
- package/dist/entities/contact-write.d.ts +22 -0
- package/dist/entities/contact-write.js +6 -0
- package/dist/entities/conversation-preparation.d.ts +63 -0
- package/dist/entities/conversation-preparation.js +23 -0
- package/dist/entities/conversation-results.d.ts +567 -0
- package/dist/entities/conversation-results.js +94 -0
- package/dist/entities/document-link.d.ts +13 -0
- package/dist/entities/document-link.js +11 -0
- package/dist/entities/extraction-report.d.ts +95 -0
- package/dist/entities/extraction-report.js +34 -0
- package/dist/entities/hit.d.ts +4 -0
- package/dist/entities/hit.js +4 -0
- package/dist/entities/image-embedding.d.ts +9 -0
- package/dist/entities/image-embedding.js +10 -0
- package/dist/entities/index.d.ts +8284 -1921
- package/dist/entities/index.js +178 -3
- package/dist/entities/listener.d.ts +2150 -0
- package/dist/entities/listener.js +46 -0
- package/dist/entities/media-archive.d.ts +43 -0
- package/dist/entities/media-archive.js +32 -0
- package/dist/entities/meeting-evidence.d.ts +86 -0
- package/dist/entities/meeting-evidence.js +25 -0
- package/dist/entities/meeting-preparation-parts.d.ts +14 -0
- package/dist/entities/meeting-preparation-parts.js +15 -13
- package/dist/entities/meeting-task-proposal.d.ts +14 -0
- package/dist/entities/meeting-task-proposal.js +19 -0
- package/dist/entities/meeting.d.ts +1 -1
- package/dist/entities/meeting.js +1 -1
- package/dist/entities/mention.d.ts +63 -0
- package/dist/entities/mention.js +44 -0
- package/dist/entities/message-change.d.ts +19 -0
- package/dist/entities/message-change.js +6 -0
- package/dist/entities/message-context.d.ts +617 -0
- package/dist/entities/message-context.js +20 -0
- package/dist/entities/message-read.d.ts +73 -0
- package/dist/entities/message-read.js +58 -0
- package/dist/entities/message-transfer.d.ts +379 -0
- package/dist/entities/message-transfer.js +83 -0
- package/dist/entities/message-write.d.ts +378 -0
- package/dist/entities/message-write.js +10 -0
- package/dist/entities/model-key.d.ts +14 -0
- package/dist/entities/model-key.js +10 -0
- package/dist/entities/poll.d.ts +40 -0
- package/dist/entities/poll.js +16 -0
- package/dist/entities/private-contact.d.ts +10 -0
- package/dist/entities/private-contact.js +4 -0
- package/dist/entities/reaction-change.d.ts +7 -0
- package/dist/entities/reaction-change.js +8 -0
- package/dist/entities/recipient.d.ts +29 -0
- package/dist/entities/recipient.js +11 -0
- package/dist/entities/recorded-run.d.ts +103 -0
- package/dist/entities/recorded-run.js +44 -0
- package/dist/entities/saved-search.d.ts +4 -0
- package/dist/entities/saved-search.js +2 -0
- package/dist/entities/send-attempt.d.ts +31 -0
- package/dist/entities/send-attempt.js +29 -0
- package/dist/entities/store-maintenance.d.ts +319 -0
- package/dist/entities/store-maintenance.js +145 -0
- package/dist/entities/store-records.d.ts +442 -0
- package/dist/entities/store-records.js +99 -0
- package/dist/entities/tag-change.d.ts +74 -0
- package/dist/entities/tag-change.js +31 -0
- package/dist/entities/task-pair.d.ts +31 -0
- package/dist/entities/task-pair.js +23 -0
- package/dist/entities/task-results.d.ts +524 -0
- package/dist/entities/task-results.js +142 -0
- package/dist/errors/reasons.js +3 -0
- package/dist/mcp/agent.d.ts +3 -1
- package/dist/mcp/agent.js +17 -10
- package/dist/mcp/bot/server.d.ts +2 -0
- package/dist/mcp/bot/server.js +1 -0
- package/dist/mcp/server.js +3 -1
- package/dist/mcp/tools/attachments.js +1 -0
- package/dist/meetings/cli/index.js +4 -2
- package/dist/meetings/cli/passage.js +1 -0
- package/dist/meetings/index.d.ts +2 -1
- package/dist/meetings/index.js +1 -0
- package/dist/meetings/links.d.ts +16 -0
- package/dist/meetings/read-answers.d.ts +185 -0
- package/dist/meetings/read-answers.js +213 -0
- package/dist/ports/messenger.d.ts +10 -1
- package/dist/runs/run.d.ts +10 -2
- package/dist/runs/run.js +11 -3
- package/dist/sends/guard.d.ts +1 -1
- package/dist/sends/guard.js +9 -5
- package/dist/sends/journal.d.ts +9 -2
- package/dist/sends/journal.js +9 -2
- package/dist/sends/permissions.d.ts +5 -1
- package/dist/sends/permissions.js +42 -2
- package/dist/sends/recipients.d.ts +8 -0
- package/dist/sends/recipients.js +9 -0
- package/dist/sends/upload.d.ts +4 -4
- package/dist/sends/upload.js +24 -20
- package/dist/services/admin-statistics.js +3 -3
- package/dist/services/chat-documents.d.ts +1 -0
- package/dist/services/chat-documents.js +9 -3
- package/dist/services/chats.d.ts +1 -1
- package/dist/services/chats.js +5 -2
- package/dist/services/documents/mirrors.d.ts +98 -0
- package/dist/services/documents/mirrors.js +144 -0
- package/dist/services/meeting-links.d.ts +2 -17
- package/dist/services/messages.js +1 -0
- package/dist/services/search-all-meetings.js +21 -8
- package/dist/services/task-rules.d.ts +1 -1
- package/dist/services/tasks.d.ts +2 -2
- package/dist/services/tasks.js +1 -1
- package/dist/store/exports.d.ts +1 -1
- package/dist/store/exports.js +1 -1
- package/dist/store/messages.d.ts +3 -1
- package/dist/store/messages.js +2 -2
- package/dist/store/roster.d.ts +4 -1
- package/dist/store/roster.js +2 -2
- package/dist/store/sqlite/chats.d.ts +12 -0
- package/dist/store/sqlite/chats.js +1 -0
- package/dist/store/sqlite/document-links.d.ts +1 -1
- package/dist/store/sqlite/document-links.js +5 -5
- package/dist/store/sqlite/drizzle/bun.js +3 -3
- package/dist/store/sqlite/drizzle/bun.js.map +1 -1
- package/dist/store/sqlite/drizzle/core.js +2 -2
- package/dist/store/sqlite/drizzle/core.js.map +1 -1
- package/dist/store/sqlite/drizzle/node.js +3 -3
- package/dist/store/sqlite/drizzle/node.js.map +1 -1
- package/dist/store/sqlite/drizzle/{shared-O7CTKP45.js → shared-HKXF6HKX.js} +57 -57
- package/dist/store/sqlite/drizzle/{shared-O7CTKP45.js.map → shared-HKXF6HKX.js.map} +1 -1
- package/dist/store/sqlite/involvements.js +2 -1
- package/dist/store/sqlite/roster.d.ts +2 -1
- package/dist/store/sqlite/roster.js +3 -2
- package/package.json +2 -2
- package/parity.json +14 -69
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { isoTime, reference } from "@wirecat/cli-base/standards";
|
|
2
|
+
import * as v from "valibot";
|
|
3
|
+
import { formatMeetingReference } from "./reference.js";
|
|
4
|
+
const text = v.string();
|
|
5
|
+
const count = v.pipe(v.number(), v.integer(), v.minValue(0));
|
|
6
|
+
const id = v.pipe(v.number(), v.integer(), v.minValue(1));
|
|
7
|
+
const iso = (ms) => (ms === null ? null : new Date(ms).toISOString());
|
|
8
|
+
const meetingRef = (accountId, meetingId) => formatMeetingReference({ type: "meeting", accountId, meetingId });
|
|
9
|
+
/** One meeting in `review`: what the store holds about it, never transcript text. */
|
|
10
|
+
export const meetingReview = v.strictObject({
|
|
11
|
+
ref: reference,
|
|
12
|
+
id: v.pipe(v.string(), v.minLength(1)),
|
|
13
|
+
title: v.nullable(text),
|
|
14
|
+
started_at: v.nullable(isoTime),
|
|
15
|
+
duration_ms: v.nullable(count),
|
|
16
|
+
participants_count: v.nullable(count),
|
|
17
|
+
projects: v.array(text),
|
|
18
|
+
/** Null when the meeting has no current transcript. */
|
|
19
|
+
transcript: v.nullable(v.strictObject({ lines: count, words: count })),
|
|
20
|
+
/** The three who talked longest. */
|
|
21
|
+
speakers: v.array(v.strictObject({ name: v.nullable(text), talk_ms: count })),
|
|
22
|
+
/** The newest stored summary; null when there is none. */
|
|
23
|
+
summary: v.nullable(v.strictObject({
|
|
24
|
+
source: text,
|
|
25
|
+
title: v.nullable(text),
|
|
26
|
+
overview: v.nullable(text),
|
|
27
|
+
next_steps: v.array(text),
|
|
28
|
+
url: v.nullable(text),
|
|
29
|
+
})),
|
|
30
|
+
});
|
|
31
|
+
export const meetingReviewItem = (one) => ({
|
|
32
|
+
ref: meetingRef(one.accountId, one.meetingId),
|
|
33
|
+
id: String(one.meetingId),
|
|
34
|
+
title: one.title,
|
|
35
|
+
started_at: iso(one.startedAt),
|
|
36
|
+
duration_ms: one.durationMs,
|
|
37
|
+
participants_count: one.participantsCount,
|
|
38
|
+
projects: one.projects,
|
|
39
|
+
transcript: one.transcript,
|
|
40
|
+
speakers: one.speakers.map((speaker) => ({ name: speaker.name, talk_ms: speaker.talkMs })),
|
|
41
|
+
summary: one.summary && {
|
|
42
|
+
source: one.summary.source,
|
|
43
|
+
title: one.summary.title,
|
|
44
|
+
overview: one.summary.overview,
|
|
45
|
+
next_steps: one.summary.nextSteps,
|
|
46
|
+
url: one.summary.url,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
/** A link shared in meeting chat, with where and how often; `mentions list` answers these. */
|
|
50
|
+
export const meetingLink = v.strictObject({
|
|
51
|
+
url: text,
|
|
52
|
+
domain: v.nullable(text),
|
|
53
|
+
/** The Google Docs, Sheets, Slides, Forms or Drive file it names. */
|
|
54
|
+
document_id: v.nullable(text),
|
|
55
|
+
count,
|
|
56
|
+
first_at: isoTime,
|
|
57
|
+
last_at: isoTime,
|
|
58
|
+
senders: v.array(text),
|
|
59
|
+
meetings: v.array(v.strictObject({ ref: reference, title: v.nullable(text) })),
|
|
60
|
+
});
|
|
61
|
+
export const meetingLinkItem = (link) => ({
|
|
62
|
+
url: link.url,
|
|
63
|
+
domain: link.domain,
|
|
64
|
+
document_id: link.documentId,
|
|
65
|
+
count: link.count,
|
|
66
|
+
first_at: new Date(link.firstAt).toISOString(),
|
|
67
|
+
last_at: new Date(link.lastAt).toISOString(),
|
|
68
|
+
senders: link.senders,
|
|
69
|
+
meetings: link.meetings.map((one) => ({ ref: meetingRef(one.accountId, one.meetingId), title: one.title })),
|
|
70
|
+
});
|
|
71
|
+
/** One speaker's talk over the selected meetings; `stats meetings top` answers these. */
|
|
72
|
+
export const meetingSpeaker = v.strictObject({
|
|
73
|
+
/** `person:<id>`, `identity:<id>`, `name:<lowercased name>` or `unknown`. */
|
|
74
|
+
key: text,
|
|
75
|
+
name: v.nullable(text),
|
|
76
|
+
person_id: v.nullable(id),
|
|
77
|
+
identity_id: v.nullable(id),
|
|
78
|
+
meetings: count,
|
|
79
|
+
lines: count,
|
|
80
|
+
words: count,
|
|
81
|
+
talk_ms: count,
|
|
82
|
+
/** This speaker's part of all talk time, 0–1. */
|
|
83
|
+
share: v.pipe(v.number(), v.minValue(0), v.maxValue(1)),
|
|
84
|
+
});
|
|
85
|
+
export const meetingSpeakerItem = (one) => ({
|
|
86
|
+
key: one.key,
|
|
87
|
+
name: one.name,
|
|
88
|
+
person_id: one.personId,
|
|
89
|
+
identity_id: one.identityId,
|
|
90
|
+
meetings: one.meetings,
|
|
91
|
+
lines: one.lines,
|
|
92
|
+
words: one.words,
|
|
93
|
+
talk_ms: one.talkMs,
|
|
94
|
+
share: one.share,
|
|
95
|
+
});
|
|
96
|
+
/** `stats meetings show`: how much was said over the selected meetings, by week, with the top speakers. */
|
|
97
|
+
export const meetingStats = v.strictObject({
|
|
98
|
+
time_zone: text,
|
|
99
|
+
meetings: count,
|
|
100
|
+
lines: count,
|
|
101
|
+
words: count,
|
|
102
|
+
talk_ms: count,
|
|
103
|
+
/** `week_start` is the Monday, as a date in `time_zone`. */
|
|
104
|
+
weeks: v.array(v.strictObject({ week_start: text, meetings: count, talk_ms: count })),
|
|
105
|
+
top_speakers: v.array(meetingSpeaker),
|
|
106
|
+
});
|
|
107
|
+
export const meetingStatsItem = (found, top = 10) => ({
|
|
108
|
+
time_zone: found.timeZone,
|
|
109
|
+
meetings: found.meetings,
|
|
110
|
+
lines: found.lines,
|
|
111
|
+
words: found.words,
|
|
112
|
+
talk_ms: found.talkMs,
|
|
113
|
+
weeks: found.weeks.map((week) => ({ week_start: week.weekStart, meetings: week.meetings, talk_ms: week.talkMs })),
|
|
114
|
+
top_speakers: found.speakers.slice(0, top).map(meetingSpeakerItem),
|
|
115
|
+
});
|
|
116
|
+
const truncated = (...why) => v.nullable(v.picklist(why));
|
|
117
|
+
/** `meetings people`: one page of participant observations whose name or email matches. */
|
|
118
|
+
export const meetingParticipants = v.strictObject({
|
|
119
|
+
schema_version: v.literal(1),
|
|
120
|
+
kind: v.literal("meeting_participants"),
|
|
121
|
+
account_id: id,
|
|
122
|
+
query: text,
|
|
123
|
+
items: v.array(v.strictObject({
|
|
124
|
+
participant_id: id,
|
|
125
|
+
meeting_id: id,
|
|
126
|
+
identity_id: id,
|
|
127
|
+
display_name: v.nullable(text),
|
|
128
|
+
email: v.nullable(text),
|
|
129
|
+
})),
|
|
130
|
+
/** Several identities on one page are observations, never speaker attribution or person links. */
|
|
131
|
+
ambiguity: v.strictObject({ scope: v.literal("page"), multiple_identities: v.boolean(), identity_ids: v.array(id) }),
|
|
132
|
+
coverage: v.strictObject({
|
|
133
|
+
scanned: count,
|
|
134
|
+
read_bytes: count,
|
|
135
|
+
archive_completeness: v.literal("unknown"),
|
|
136
|
+
truncated_by: truncated("limit", "scan_limit", "read_bytes"),
|
|
137
|
+
}),
|
|
138
|
+
page: v.strictObject({
|
|
139
|
+
mode: v.literal("cursor"),
|
|
140
|
+
limit: count,
|
|
141
|
+
has_more: v.boolean(),
|
|
142
|
+
next_cursor: v.optional(text),
|
|
143
|
+
}),
|
|
144
|
+
});
|
|
145
|
+
const revision = v.picklist(["current", "superseded"]);
|
|
146
|
+
/** `meetings passage`: a transcript line with the lines around it. */
|
|
147
|
+
export const meetingPassage = v.strictObject({
|
|
148
|
+
schema_version: v.literal(1),
|
|
149
|
+
kind: v.literal("meeting_context"),
|
|
150
|
+
source: reference,
|
|
151
|
+
target_ref: reference,
|
|
152
|
+
target_fingerprint: text,
|
|
153
|
+
fingerprint: text,
|
|
154
|
+
revision,
|
|
155
|
+
limits: v.strictObject({ before: count, after: count, bytes: count, max_read_bytes: count }),
|
|
156
|
+
content_bytes: count,
|
|
157
|
+
coverage: v.strictObject({
|
|
158
|
+
included: count,
|
|
159
|
+
before: count,
|
|
160
|
+
after: count,
|
|
161
|
+
has_before: v.boolean(),
|
|
162
|
+
has_after: v.boolean(),
|
|
163
|
+
truncated_by: truncated("bytes"),
|
|
164
|
+
before_ref: v.nullable(reference),
|
|
165
|
+
after_ref: v.nullable(reference),
|
|
166
|
+
continuation: v.literal("anchor-inclusive"),
|
|
167
|
+
archive_completeness: v.literal("unknown"),
|
|
168
|
+
input: v.literal("bounded-meeting-context"),
|
|
169
|
+
}),
|
|
170
|
+
items: v.array(v.strictObject({
|
|
171
|
+
ref: reference,
|
|
172
|
+
fingerprint: text,
|
|
173
|
+
transcript_id: id,
|
|
174
|
+
content_hash: v.nullable(text),
|
|
175
|
+
position: count,
|
|
176
|
+
start_ms: count,
|
|
177
|
+
end_ms: count,
|
|
178
|
+
speaker_participant_id: v.nullable(id),
|
|
179
|
+
speaker_name: v.nullable(text),
|
|
180
|
+
text,
|
|
181
|
+
revision,
|
|
182
|
+
})),
|
|
183
|
+
});
|
|
184
|
+
/** A stored event a meeting belongs to; `id` is what `meetings update --event-id` takes. */
|
|
185
|
+
export const event = v.strictObject({
|
|
186
|
+
id: v.pipe(v.string(), v.minLength(1)),
|
|
187
|
+
event_series_id: v.nullable(text),
|
|
188
|
+
title: v.nullable(text),
|
|
189
|
+
description: v.nullable(text),
|
|
190
|
+
location: v.nullable(text),
|
|
191
|
+
starts_at: v.nullable(isoTime),
|
|
192
|
+
ends_at: v.nullable(isoTime),
|
|
193
|
+
timezone: v.nullable(text),
|
|
194
|
+
/** `auto`: made from a meeting; `owner`: made by hand. */
|
|
195
|
+
origin: v.picklist(["auto", "owner"]),
|
|
196
|
+
deleted_at: v.nullable(isoTime),
|
|
197
|
+
created_at: isoTime,
|
|
198
|
+
updated_at: isoTime,
|
|
199
|
+
});
|
|
200
|
+
export const eventItem = (one) => ({
|
|
201
|
+
id: String(one.id),
|
|
202
|
+
event_series_id: one.eventSeriesId === null ? null : String(one.eventSeriesId),
|
|
203
|
+
title: one.title,
|
|
204
|
+
description: one.description,
|
|
205
|
+
location: one.location,
|
|
206
|
+
starts_at: iso(one.startsAt),
|
|
207
|
+
ends_at: iso(one.endsAt),
|
|
208
|
+
timezone: one.timezone,
|
|
209
|
+
origin: one.origin,
|
|
210
|
+
deleted_at: iso(one.deletedAt),
|
|
211
|
+
created_at: new Date(one.createdAt).toISOString(),
|
|
212
|
+
updated_at: new Date(one.updatedAt).toISOString(),
|
|
213
|
+
});
|
|
@@ -41,7 +41,10 @@ export interface SendOptions {
|
|
|
41
41
|
formatting?: TextSpan[];
|
|
42
42
|
/** ISO time: the messenger holds it and sends it then, under a new id. */
|
|
43
43
|
at?: string;
|
|
44
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Sent in one message, `text` as the caption. More than one is an album: photos first, then files, each in the
|
|
46
|
+
* order given. An adapter whose messenger cannot send one refuses it with `validation_error` before anything goes.
|
|
47
|
+
*/
|
|
45
48
|
attachments?: Upload[];
|
|
46
49
|
/** One sticker by id, alone: no text, no attachment. Only where `Messenger.stickers` is on. */
|
|
47
50
|
sticker?: Id;
|
|
@@ -82,7 +85,13 @@ export interface NewPoll {
|
|
|
82
85
|
closeAfter?: number;
|
|
83
86
|
}
|
|
84
87
|
export interface Sent {
|
|
88
|
+
/** The message sent; for an album, its first. */
|
|
85
89
|
message: Message;
|
|
90
|
+
/**
|
|
91
|
+
* For an album, every message it made, in order, `message` among them. Unset for a single message, and where an
|
|
92
|
+
* adapter has not learned to answer with them yet.
|
|
93
|
+
*/
|
|
94
|
+
messages?: Message[];
|
|
86
95
|
/** The send's identity. Repeat it after an unknown outcome, never a new one. */
|
|
87
96
|
sendId: string;
|
|
88
97
|
}
|
package/dist/runs/run.d.ts
CHANGED
|
@@ -87,7 +87,7 @@ export declare const runsDirFor: (app: AppIdentity, env?: NodeJS.ProcessEnv) =>
|
|
|
87
87
|
* `run.json` is written **now**, with `status: "running"`, and again by `finish`. A directory
|
|
88
88
|
* holding events and no metadata is a special case `runs list` would carry forever.
|
|
89
89
|
*
|
|
90
|
-
* Each write also appends the metadata to the day's `index.jsonl`, which `runs list` and `runs
|
|
90
|
+
* Each write also appends the metadata to the day's `index.jsonl`, which `runs list` and `search runs`
|
|
91
91
|
* read instead of every `run.json`. It is only a cache: pruned with its day, and a day without one is read
|
|
92
92
|
* the slow way.
|
|
93
93
|
*/
|
|
@@ -95,13 +95,21 @@ export declare const startRun: (options: StartRunOptions) => Run;
|
|
|
95
95
|
export declare const runtime: () => string;
|
|
96
96
|
/** `20260920T084431Z-chats-list-a81f2c` — sortable, and readable without opening it. */
|
|
97
97
|
export declare const newRunId: (startedAt: Date, command: string) => string;
|
|
98
|
+
export interface RunListFilter {
|
|
99
|
+
runId?: string;
|
|
100
|
+
command?: string;
|
|
101
|
+
profile?: string;
|
|
102
|
+
status?: RunMetadata["status"];
|
|
103
|
+
errorCode?: string;
|
|
104
|
+
}
|
|
98
105
|
/**
|
|
99
106
|
* Newest first, which is the order anyone asking "what did I just do" wants. Days before `since` are
|
|
100
107
|
* not opened, nor any day once `atLeast` runs are in hand.
|
|
101
108
|
*/
|
|
102
|
-
export declare const listRuns: (runsDir: string, { since, atLeast }?: {
|
|
109
|
+
export declare const listRuns: (runsDir: string, { since, atLeast, filter, }?: {
|
|
103
110
|
since?: number;
|
|
104
111
|
atLeast?: number;
|
|
112
|
+
filter?: RunListFilter;
|
|
105
113
|
}) => RunMetadata[];
|
|
106
114
|
export declare const findRun: (runsDir: string, id: string) => {
|
|
107
115
|
dir: string;
|
package/dist/runs/run.js
CHANGED
|
@@ -19,7 +19,7 @@ export const runsDirFor = (app, env = process.env) => join(resolvePaths({ appNam
|
|
|
19
19
|
* `run.json` is written **now**, with `status: "running"`, and again by `finish`. A directory
|
|
20
20
|
* holding events and no metadata is a special case `runs list` would carry forever.
|
|
21
21
|
*
|
|
22
|
-
* Each write also appends the metadata to the day's `index.jsonl`, which `runs list` and `runs
|
|
22
|
+
* Each write also appends the metadata to the day's `index.jsonl`, which `runs list` and `search runs`
|
|
23
23
|
* read instead of every `run.json`. It is only a cache: pruned with its day, and a day without one is read
|
|
24
24
|
* the slow way.
|
|
25
25
|
*/
|
|
@@ -124,17 +124,25 @@ const writeRunFile = (dir, metadata) => {
|
|
|
124
124
|
// The index only makes reading faster; a day without one is read run by run.
|
|
125
125
|
}
|
|
126
126
|
};
|
|
127
|
+
const matchesListFilter = (run, filter) => (filter.runId === undefined || run.runId === filter.runId) &&
|
|
128
|
+
(filter.command === undefined || run.command === filter.command) &&
|
|
129
|
+
(filter.profile === undefined || run.profile === filter.profile) &&
|
|
130
|
+
(filter.status === undefined || run.status === filter.status) &&
|
|
131
|
+
(filter.errorCode === undefined || run.errorCode === filter.errorCode);
|
|
127
132
|
/**
|
|
128
133
|
* Newest first, which is the order anyone asking "what did I just do" wants. Days before `since` are
|
|
129
134
|
* not opened, nor any day once `atLeast` runs are in hand.
|
|
130
135
|
*/
|
|
131
|
-
export const listRuns = (runsDir, { since, atLeast = Number.POSITIVE_INFINITY } = {}) => {
|
|
136
|
+
export const listRuns = (runsDir, { since, atLeast = Number.POSITIVE_INFINITY, filter = {}, } = {}) => {
|
|
132
137
|
const first = since === undefined ? undefined : day(new Date(since));
|
|
133
138
|
const runs = [];
|
|
134
139
|
for (const date of days(runsDir).sort().reverse()) {
|
|
135
140
|
if ((first !== undefined && date < first) || runs.length >= atLeast)
|
|
136
141
|
break;
|
|
137
|
-
|
|
142
|
+
for (const metadata of runsOfDay(join(runsDir, date))) {
|
|
143
|
+
if (matchesListFilter(metadata, filter))
|
|
144
|
+
runs.push(metadata);
|
|
145
|
+
}
|
|
138
146
|
}
|
|
139
147
|
return runs;
|
|
140
148
|
};
|
package/dist/sends/guard.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface GuardRequest {
|
|
|
12
12
|
chatId: Id | null;
|
|
13
13
|
kind?: SendKind;
|
|
14
14
|
action?: ChatAction | AccountAction;
|
|
15
|
-
/** Messages a deletion names, or people a chat change adds. */
|
|
15
|
+
/** Messages a deletion names or an album sends, or people a chat change adds. */
|
|
16
16
|
count?: number;
|
|
17
17
|
sendId?: string;
|
|
18
18
|
sendAs?: Id;
|
package/dist/sends/guard.js
CHANGED
|
@@ -40,11 +40,11 @@ const countsTowardLimit = ({ kind = "message", action, notify }) => kind === "me
|
|
|
40
40
|
action === "join" ||
|
|
41
41
|
action === "contact-import";
|
|
42
42
|
/**
|
|
43
|
-
* Each deleted message, each person added to a group or let in by a
|
|
44
|
-
* counts as one.
|
|
43
|
+
* Each deleted message, each photo or file of an album (`NEED-1660`), each person added to a group or let in by a
|
|
44
|
+
* request, and each imported number counts as one.
|
|
45
45
|
*/
|
|
46
|
-
const weightOf = ({ kind, action, count, people }) => kind === "delete"
|
|
47
|
-
? (count ?? 1)
|
|
46
|
+
const weightOf = ({ kind = "message", action, count, people }) => kind === "delete" || (kind === "message" && action === undefined)
|
|
47
|
+
? Math.max(1, count ?? 1)
|
|
48
48
|
: action === "create" || action === "members.add" || action === "requests.accept" || action === "contact-import"
|
|
49
49
|
? Math.max(1, people ?? count ?? 1)
|
|
50
50
|
: 1;
|
|
@@ -180,7 +180,11 @@ export const sendGuard = ({ profile, recipients, command = recipients.command, r
|
|
|
180
180
|
reservation: id,
|
|
181
181
|
...(kind ? { kind } : {}),
|
|
182
182
|
...(action ? { action } : {}),
|
|
183
|
-
...(asked.people === undefined
|
|
183
|
+
...(asked.people === undefined
|
|
184
|
+
? {}
|
|
185
|
+
: kind === "delete" || kind === "message" || kind === undefined
|
|
186
|
+
? { count: asked.people }
|
|
187
|
+
: { people: asked.people }),
|
|
184
188
|
...(sendId === undefined ? {} : { sendId }),
|
|
185
189
|
...(operationId === undefined ? {} : { operationId }),
|
|
186
190
|
...(scheduledFor ? { scheduledFor } : {}),
|
package/dist/sends/journal.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface SendEntry {
|
|
|
23
23
|
messageId?: Id;
|
|
24
24
|
/** The command a write belongs to, where it named one: `polls.create` sets a poll apart from a message. */
|
|
25
25
|
key?: string;
|
|
26
|
-
/** How many messages a `delete` entry named — each one counts toward the hourly limit. */
|
|
26
|
+
/** How many messages a `delete` entry named, or an album sent — each one counts toward the hourly limit. */
|
|
27
27
|
count?: number;
|
|
28
28
|
forEveryone?: boolean;
|
|
29
29
|
/**
|
|
@@ -59,6 +59,13 @@ export interface SendEntry {
|
|
|
59
59
|
reservation?: string;
|
|
60
60
|
errorCode?: string;
|
|
61
61
|
}
|
|
62
|
+
export interface SendFilter {
|
|
63
|
+
outcome?: SendOutcome;
|
|
64
|
+
chatId?: Id;
|
|
65
|
+
operationId?: string;
|
|
66
|
+
errorCode?: string;
|
|
67
|
+
key?: string;
|
|
68
|
+
}
|
|
62
69
|
/**
|
|
63
70
|
* `<state dir>/sends/<profile>.jsonl` — beside `runs`, never in the cache directory, so that
|
|
64
71
|
* clearing a cache cannot erase what was sent in the owner's name.
|
|
@@ -88,5 +95,5 @@ export declare class SendJournal {
|
|
|
88
95
|
* Oldest first, each reservation folded into its outcome; one with no outcome yet is in flight,
|
|
89
96
|
* or its process died. A line that does not parse is skipped: a torn last write must not block sending.
|
|
90
97
|
*/
|
|
91
|
-
entries(): SendEntry[];
|
|
98
|
+
entries(filter?: SendFilter): SendEntry[];
|
|
92
99
|
}
|
package/dist/sends/journal.js
CHANGED
|
@@ -54,13 +54,20 @@ export class SendJournal {
|
|
|
54
54
|
* Oldest first, each reservation folded into its outcome; one with no outcome yet is in flight,
|
|
55
55
|
* or its process died. A line that does not parse is skipped: a torn last write must not block sending.
|
|
56
56
|
*/
|
|
57
|
-
entries() {
|
|
57
|
+
entries(filter) {
|
|
58
58
|
const lines = this.#lines();
|
|
59
59
|
const settled = new Set(lines.filter((entry) => entry.outcome !== "reserved").map((entry) => entry.reservation));
|
|
60
|
-
|
|
60
|
+
const entries = lines
|
|
61
61
|
.filter((entry) => entry.outcome !== "reserved" || !settled.has(entry.reservation))
|
|
62
62
|
.map(({ reservation, ...entry }) => (entry.outcome === "reserved" ? { ...entry, reservation } : entry))
|
|
63
63
|
.map(withSendId);
|
|
64
|
+
return filter === undefined
|
|
65
|
+
? entries
|
|
66
|
+
: entries.filter((entry) => (filter.outcome === undefined || entry.outcome === filter.outcome) &&
|
|
67
|
+
(filter.chatId === undefined || entry.chatId === filter.chatId) &&
|
|
68
|
+
(filter.operationId === undefined || entry.operationId === filter.operationId) &&
|
|
69
|
+
(filter.errorCode === undefined || entry.errorCode === filter.errorCode) &&
|
|
70
|
+
(filter.key === undefined || entry.key === filter.key));
|
|
64
71
|
}
|
|
65
72
|
#lines() {
|
|
66
73
|
let text;
|
|
@@ -17,8 +17,12 @@ export type PermissionKey = string;
|
|
|
17
17
|
export declare const skipFlagFor: (key: string) => "--allow-dangerous" | "--yes";
|
|
18
18
|
/** Only what cannot be undone asks; the tool is meant to work without questions (owner, NEED-460). */
|
|
19
19
|
export declare const DEFAULT_PERMISSIONS: Readonly<Record<PermissionKey, Level>>;
|
|
20
|
+
/** Lets an MCP server offer the commands that change what a later run may do (`grantsAuthority`). */
|
|
21
|
+
export declare const AUTHORITY_KEY: PermissionKey;
|
|
22
|
+
/** Whether these levels let an agent run a command that changes what a later run may do; `ask` does, as for any write. */
|
|
23
|
+
export declare const authorityAllowed: (levels: Readonly<Record<PermissionKey, Level>>) => boolean;
|
|
20
24
|
/** The resources at the top of the command tree, which `readOnly` and `allow` turn read-only as a whole. */
|
|
21
|
-
export declare const RESOURCES: readonly ["messages", "reactions", "polls", "topics", "chats", "contacts", "account", "bot", "conversations", "tags", "mentions", "search", "searches", "tasks", "replies", "attachments", "stats", "store", "metadata"];
|
|
25
|
+
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"];
|
|
22
26
|
/** The command path an old `allow` word stood for, as one key — what an MCP tool's `permission` is checked against. */
|
|
23
27
|
export declare const keyOfWord: (word: Permission) => PermissionKey;
|
|
24
28
|
/**
|
|
@@ -77,6 +77,35 @@ export const DEFAULT_PERMISSIONS = {
|
|
|
77
77
|
"chats.clear": "ask",
|
|
78
78
|
// NEED-568: a rule writes to people with nobody typing a command; it waits for the owner's own allow.
|
|
79
79
|
"replies.send": "deny",
|
|
80
|
+
// NEED-1662: an agent that changes settings, schedules or recipients could lift its own limits; the owner opts in.
|
|
81
|
+
"mcp.authority": "deny",
|
|
82
|
+
// NEED-1663: writes no level stopped before, so MCP could not offer them; over MCP `ask` goes ahead.
|
|
83
|
+
"store.backup": "ask",
|
|
84
|
+
"store.restore": "ask",
|
|
85
|
+
"store.migrate": "ask",
|
|
86
|
+
"store.repair": "ask",
|
|
87
|
+
"store.reset": "ask",
|
|
88
|
+
"store.clear": "ask",
|
|
89
|
+
"store.decrypt": "ask",
|
|
90
|
+
"store.copies.delete": "ask",
|
|
91
|
+
"server.start": "ask",
|
|
92
|
+
"server.stop": "ask",
|
|
93
|
+
"server.restart": "ask",
|
|
94
|
+
"models.audio.download": "ask",
|
|
95
|
+
"models.text.download": "ask",
|
|
96
|
+
"images.models.download": "ask",
|
|
97
|
+
"schedule.run": "ask",
|
|
98
|
+
"schedule.exec": "ask",
|
|
99
|
+
"doctor.report.create": "ask",
|
|
100
|
+
};
|
|
101
|
+
/** Commands under a housekeeping word, or store maintenance, that still have a key of their own: their path. */
|
|
102
|
+
const KEYED_UNDER_HOUSEKEEPING = new Set(Object.keys(DEFAULT_PERMISSIONS).filter((key) => /^(store|server|models|images|schedule|doctor)\./.test(key)));
|
|
103
|
+
/** Lets an MCP server offer the commands that change what a later run may do (`grantsAuthority`). */
|
|
104
|
+
export const AUTHORITY_KEY = "mcp.authority";
|
|
105
|
+
/** Whether these levels let an agent run a command that changes what a later run may do; `ask` does, as for any write. */
|
|
106
|
+
export const authorityAllowed = (levels) => {
|
|
107
|
+
const { level } = levelFor(levels, AUTHORITY_KEY);
|
|
108
|
+
return level === "allow" || level === "ask";
|
|
80
109
|
};
|
|
81
110
|
/** The resources at the top of the command tree, which `readOnly` and `allow` turn read-only as a whole. */
|
|
82
111
|
export const RESOURCES = [
|
|
@@ -99,6 +128,12 @@ export const RESOURCES = [
|
|
|
99
128
|
"stats",
|
|
100
129
|
"store",
|
|
101
130
|
"metadata",
|
|
131
|
+
"mcp",
|
|
132
|
+
"server",
|
|
133
|
+
"models",
|
|
134
|
+
"schedule",
|
|
135
|
+
"doctor",
|
|
136
|
+
"images",
|
|
102
137
|
];
|
|
103
138
|
const OLD_WORDS = {
|
|
104
139
|
send: ["messages.send", "polls.create", "chats.start"],
|
|
@@ -365,7 +400,7 @@ const HOUSEKEEPING = new Set([
|
|
|
365
400
|
]);
|
|
366
401
|
/** Under `bot`: its token, its names, its lists and its MCP server. */
|
|
367
402
|
const BOT_HOUSEKEEPING = new Set(["auth", "list", "sends", "recipients", "mcp"]);
|
|
368
|
-
const STORE_MAINTENANCE = new Set(["info", "check", "
|
|
403
|
+
const STORE_MAINTENANCE = new Set(["info", "check", "copies"]);
|
|
369
404
|
/** Commands outside `messages` that print what people wrote, so `deny messages` reaches them too. */
|
|
370
405
|
const SHOW_MESSAGES = new Set(["inbox", "review", "watch", "serve", "store", "conversations"]);
|
|
371
406
|
/** Before every search moved under `search` (2026-10-09). */
|
|
@@ -395,6 +430,9 @@ export const readKeysForCommand = (path) => {
|
|
|
395
430
|
return [key, "messages"];
|
|
396
431
|
if (path[0] === "store" && path[1] === "gaps" && path[2] === "repair")
|
|
397
432
|
return [key, "messages"];
|
|
433
|
+
// Checked as `messages` before they had keys of their own: `deny messages` keeps stopping them.
|
|
434
|
+
if (path[0] === "store" && ["reset", "clear", "decrypt"].includes(path[1] ?? ""))
|
|
435
|
+
return [key, "messages"];
|
|
398
436
|
// A search shows what people wrote, so `deny messages` reaches every leaf that reads messages.
|
|
399
437
|
if (path[0] === "search") {
|
|
400
438
|
if (path[1] === "notes")
|
|
@@ -428,7 +466,9 @@ export const keyForCommand = (path) => {
|
|
|
428
466
|
const inner = keyForCommand(path.slice(1));
|
|
429
467
|
return `bot.${inner ?? path.slice(1).join(".")}`;
|
|
430
468
|
}
|
|
431
|
-
if (
|
|
469
|
+
if (KEYED_UNDER_HOUSEKEEPING.has(path.join(".")))
|
|
470
|
+
return path.join(".");
|
|
471
|
+
if (top === undefined || HOUSEKEEPING.has(top) || (top === "search" && next === "runs"))
|
|
432
472
|
return null;
|
|
433
473
|
// Picture models moved from `models image` (0.270.0); a model is housekeeping wherever it is listed.
|
|
434
474
|
if (top === "images" && next === "models")
|
|
@@ -7,6 +7,10 @@ export interface Recipient {
|
|
|
7
7
|
partnerId?: Id;
|
|
8
8
|
addedAt: string;
|
|
9
9
|
}
|
|
10
|
+
export interface RecipientFilter {
|
|
11
|
+
id?: Id;
|
|
12
|
+
title?: string;
|
|
13
|
+
}
|
|
10
14
|
/**
|
|
11
15
|
* A state file, not a setting: the configuration schema has deliberately no place for a chat id,
|
|
12
16
|
* and this list is data about a profile rather than a preference.
|
|
@@ -25,6 +29,10 @@ export declare class RecipientList {
|
|
|
25
29
|
constructor(path: string, command: string, lockWaitMs?: number | undefined);
|
|
26
30
|
/** `undefined` when the list is off. A file that cannot be read refuses rather than opening up. */
|
|
27
31
|
read(): Recipient[] | undefined;
|
|
32
|
+
list(filter?: RecipientFilter): {
|
|
33
|
+
state: "off" | "empty" | "on";
|
|
34
|
+
items: Recipient[];
|
|
35
|
+
};
|
|
28
36
|
/** Whether it was new. Adding one again fills in a partner an older entry was stored without. */
|
|
29
37
|
add(recipient: Recipient): boolean;
|
|
30
38
|
/** `add` waiting for the lock on timers: `recipients add` connects, so it runs inside `serve` when one holds the account. */
|
package/dist/sends/recipients.js
CHANGED
|
@@ -36,6 +36,15 @@ export class RecipientList {
|
|
|
36
36
|
`fix the file or turn the list off with \`${this.command} recipients clear\``);
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
+
list(filter = {}) {
|
|
40
|
+
const chats = this.read();
|
|
41
|
+
// A query that matches nothing must not describe a nonempty allowlist as allowing nowhere.
|
|
42
|
+
return {
|
|
43
|
+
state: chats === undefined ? "off" : chats.length === 0 ? "empty" : "on",
|
|
44
|
+
items: (chats ?? []).filter((chat) => (filter.id === undefined || chat.id === filter.id) &&
|
|
45
|
+
(filter.title === undefined || chat.title?.toLocaleLowerCase() === filter.title.toLocaleLowerCase())),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
39
48
|
/** Whether it was new. Adding one again fills in a partner an older entry was stored without. */
|
|
40
49
|
add(recipient) {
|
|
41
50
|
return this.#locked(() => this.#added(recipient));
|
package/dist/sends/upload.d.ts
CHANGED
|
@@ -18,12 +18,12 @@ export declare const readUpload: (kind: UploadKind, path: string, { app, env, an
|
|
|
18
18
|
photoTypes?: readonly string[];
|
|
19
19
|
}) => Promise<Upload>;
|
|
20
20
|
/**
|
|
21
|
-
* What a send attaches, as `messages send` and its tool take it.
|
|
22
|
-
* messengers' own apps send one.
|
|
21
|
+
* What a send attaches, as `messages send` and its tool take it. More than one photo or file is an album: photos
|
|
22
|
+
* first, then files, each in the order given. A voice message goes alone, as the messengers' own apps send one.
|
|
23
23
|
*/
|
|
24
24
|
export declare const readAttachments: ({ photo, file, voice, asFile, filename, text, }: {
|
|
25
|
-
photo?: string;
|
|
26
|
-
file?: string;
|
|
25
|
+
photo?: string | readonly string[];
|
|
26
|
+
file?: string | readonly string[];
|
|
27
27
|
voice?: string;
|
|
28
28
|
asFile?: boolean;
|
|
29
29
|
filename?: string;
|
package/dist/sends/upload.js
CHANGED
|
@@ -197,37 +197,41 @@ export const readUpload = async (kind, path, { app, env = process.env, anyFile =
|
|
|
197
197
|
}
|
|
198
198
|
return { kind, name: basename(path), source: { kind: "file", path: resolve(path), size } };
|
|
199
199
|
};
|
|
200
|
-
const checkFilename = (filename,
|
|
201
|
-
if (
|
|
200
|
+
const checkFilename = (filename, files) => {
|
|
201
|
+
if (files.length === 0)
|
|
202
202
|
throw new CliError("validation_error", "--filename names a --file; give one");
|
|
203
|
+
if (files.length > 1)
|
|
204
|
+
throw new CliError("validation_error", "--filename names one --file; an album keeps its names");
|
|
203
205
|
if (filename.trim() === "" || /[/\\\0]/.test(filename) || filename.length > 255)
|
|
204
206
|
throw new CliError("validation_error", "--filename is a name, not a path: no / or \\, at most 255 characters");
|
|
205
207
|
};
|
|
208
|
+
const listOf = (value) => value === undefined ? [] : typeof value === "string" ? [value] : value;
|
|
206
209
|
/**
|
|
207
|
-
* What a send attaches, as `messages send` and its tool take it.
|
|
208
|
-
* messengers' own apps send one.
|
|
210
|
+
* What a send attaches, as `messages send` and its tool take it. More than one photo or file is an album: photos
|
|
211
|
+
* first, then files, each in the order given. A voice message goes alone, as the messengers' own apps send one.
|
|
209
212
|
*/
|
|
210
213
|
export const readAttachments = async ({ photo, file, voice, asFile, filename, text, }, read) => {
|
|
211
|
-
|
|
214
|
+
const photos = listOf(photo);
|
|
215
|
+
const files = listOf(file);
|
|
216
|
+
if (voice !== undefined && (photos.length > 0 || files.length > 0 || (text ?? "").trim() !== "")) {
|
|
212
217
|
throw new CliError("validation_error", "a voice message goes alone — no text, no file, no photo beside it");
|
|
213
218
|
}
|
|
214
|
-
if (asFile &&
|
|
219
|
+
if (asFile && files.length === 0)
|
|
215
220
|
throw new CliError("validation_error", "--as-file is about a --file; give one");
|
|
216
221
|
if (filename !== undefined)
|
|
217
|
-
checkFilename(filename,
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
];
|
|
222
|
+
checkFilename(filename, files);
|
|
223
|
+
const uploads = [];
|
|
224
|
+
for (const one of photos)
|
|
225
|
+
uploads.push(await readUpload("photo", one, read));
|
|
226
|
+
for (const one of files)
|
|
227
|
+
uploads.push({
|
|
228
|
+
...(await readUpload("file", one, read)),
|
|
229
|
+
...(filename === undefined ? {} : { name: filename }),
|
|
230
|
+
...(asFile ? { asFile: true } : {}),
|
|
231
|
+
});
|
|
232
|
+
if (voice !== undefined)
|
|
233
|
+
uploads.push(await readUpload("voice", voice, read));
|
|
234
|
+
return uploads;
|
|
231
235
|
};
|
|
232
236
|
/** A sticker to send from a file: a WebP image of at most `maxBytes`, behind the same file guard as `--file`. */
|
|
233
237
|
export const readStickerFile = async (path, maxBytes, read) => {
|
|
@@ -216,12 +216,12 @@ export const adminStatisticsService = (deps) => ({
|
|
|
216
216
|
cutoff,
|
|
217
217
|
olderThan: query.olderThan === undefined && pinned
|
|
218
218
|
? pinned.options.olderThan
|
|
219
|
-
: duration(query.olderThan ?? "24h", "
|
|
219
|
+
: duration(query.olderThan ?? "24h", "older_than"),
|
|
220
220
|
within: query.within === undefined && pinned ? pinned.options.within : duration(query.within ?? "7d", "--within"),
|
|
221
221
|
joinSince: window.since ?? pinned?.options.joinSince ?? cutoff - 30 * 86_400_000,
|
|
222
222
|
joinUntil: window.until ?? pinned?.options.joinUntil ?? cutoff,
|
|
223
|
-
minViews: count(query.minViews ?? pinned?.options.minViews ?? 1, "
|
|
224
|
-
maxReplies: count(query.maxReplies ?? pinned?.options.maxReplies ?? 0, "
|
|
223
|
+
minViews: count(query.minViews ?? pinned?.options.minViews ?? 1, "min_views"),
|
|
224
|
+
maxReplies: count(query.maxReplies ?? pinned?.options.maxReplies ?? 0, "max_replies"),
|
|
225
225
|
answerers: query.answerers === undefined && pinned
|
|
226
226
|
? pinned.options.answerers
|
|
227
227
|
: await answerers(store, query.answerers ?? [], prepared.execution.accounts),
|