@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
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { googleDocumentLinks } from "../domain/google-documents.js";
|
|
2
|
-
import { formatLocator } from "../domain/locator.js";
|
|
2
|
+
import { formatLocator, parseLocator } from "../domain/locator.js";
|
|
3
3
|
import { accountStore } from "./deps.js";
|
|
4
4
|
import { chatIdIn } from "./message-resolution.js";
|
|
5
5
|
export const chatDocumentsService = (deps) => ({
|
|
6
|
-
list: async (chat, { limit, offset }) => {
|
|
6
|
+
list: async (chat, { limit, offset, ref }) => {
|
|
7
7
|
const store = await accountStore(deps);
|
|
8
8
|
const chatId = await chatIdIn(deps.messenger, chat, store, { includeLeft: true });
|
|
9
|
-
const
|
|
9
|
+
const target = ref === undefined ? undefined : parseLocator(ref);
|
|
10
|
+
const sameScope = target === undefined ||
|
|
11
|
+
(target.provider === store.account.provider && target.account === store.account.account && target.chat === chatId);
|
|
12
|
+
const messages = sameScope
|
|
13
|
+
? await store.messages.documentLinkMessages(chatId, target ? { messageId: target.message } : {})
|
|
14
|
+
: [];
|
|
15
|
+
const items = messages.flatMap((message) => googleDocumentLinks(message.text).map(({ url, documentId }) => {
|
|
10
16
|
const stored = documentId === null ? undefined : message.documents.find((one) => one.fileId === documentId);
|
|
11
17
|
return {
|
|
12
18
|
url,
|
package/dist/services/chats.d.ts
CHANGED
|
@@ -79,7 +79,7 @@ export interface ChatsService {
|
|
|
79
79
|
track?: boolean;
|
|
80
80
|
}): Promise<FetchedMembers>;
|
|
81
81
|
/** The chats whose members `serve` fetches daily, from the store. */
|
|
82
|
-
tracked(): Promise<TrackedChat[]>;
|
|
82
|
+
tracked(query?: QueryAst): Promise<TrackedChat[]>;
|
|
83
83
|
/** One chat's tracking and its member counts of the last `TRACKED_DAYS` days, tracked or not. */
|
|
84
84
|
trackedChat(chat: string): Promise<{
|
|
85
85
|
chatId: Id;
|
package/dist/services/chats.js
CHANGED
|
@@ -7,7 +7,7 @@ import { syncsOneChat } from "../ports/traits.js";
|
|
|
7
7
|
import { typedCommand } from "../runs/scope.js";
|
|
8
8
|
import { codeOf, guardedWrite } from "../sends/guarded.js";
|
|
9
9
|
import { newOperationId } from "../sends/send-id.js";
|
|
10
|
-
import { CHAT_COLUMNS, queryFilter, } from "../store/store.js";
|
|
10
|
+
import { CHAT_COLUMNS, queryFilter, TRACKED_CHAT_COLUMNS, } from "../store/store.js";
|
|
11
11
|
import { chatStats } from "./chat-stats.js";
|
|
12
12
|
import { accountStore, accountStoreIfOpen, fromStore, nothingStored, unlessBusy } from "./deps.js";
|
|
13
13
|
import { memberEvents } from "./member-history.js";
|
|
@@ -210,7 +210,10 @@ export const chatsService = (deps) => ({
|
|
|
210
210
|
const tracked = (await store.roster.trackedChats()).some((one) => one.chatId === chatId);
|
|
211
211
|
return { chatId, read: members.length, participants, complete, more, ...change, tracked };
|
|
212
212
|
},
|
|
213
|
-
tracked: async () =>
|
|
213
|
+
tracked: async (query) => {
|
|
214
|
+
const where = query ? queryFilter(query, TRACKED_CHAT_COLUMNS, { command: "chats tracking list" }) : undefined;
|
|
215
|
+
return (await accountStore(deps)).roster.trackedChats(where ? { where } : {});
|
|
216
|
+
},
|
|
214
217
|
memberHistory: async (chat, { since }) => {
|
|
215
218
|
const store = await accountStore(deps);
|
|
216
219
|
const chatId = await chatIdIn(deps.messenger, chat, store, { includeLeft: true });
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { DocumentTarget, Keep, PushStatePort } from "../../documents/push-model.js";
|
|
2
|
+
import { type PushConflict, type PushOptions } from "./push.js";
|
|
3
|
+
/** A local folder mirrored into a remote folder, as the app's profile config keeps it. */
|
|
4
|
+
export interface Mirror {
|
|
5
|
+
id: string;
|
|
6
|
+
path: string;
|
|
7
|
+
folder: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const mirrorId: (root: string, folder: string) => string;
|
|
10
|
+
/**
|
|
11
|
+
* A mirror's sync state in `<dir>/<id>.json`. A missing file is a first sync; an unreadable or invalid one is
|
|
12
|
+
* refused rather than reset, since a reset would push every file again as new.
|
|
13
|
+
*/
|
|
14
|
+
export declare const fileMirrorState: <S>(dir: string, mirror: Mirror, principal: string, kinds: [string, ...string[]]) => PushStatePort<S>;
|
|
15
|
+
/** What the next sync of each mirror would create or send, from local files and the state alone. */
|
|
16
|
+
export declare const mirrorStatus: <S>(mirrors: Mirror[], kindOf: (path: string) => string | undefined, stateOf: (mirror: Mirror) => PushStatePort<S>) => Promise<{
|
|
17
|
+
id: string;
|
|
18
|
+
path: string;
|
|
19
|
+
folder: string;
|
|
20
|
+
create: string[];
|
|
21
|
+
update: string[];
|
|
22
|
+
unchanged: number;
|
|
23
|
+
orphans: string[];
|
|
24
|
+
unsupported: number;
|
|
25
|
+
remote_checked: boolean;
|
|
26
|
+
}[]>;
|
|
27
|
+
/** A conflict as the user reads it, with the remote side named the way the app names it (`google`). */
|
|
28
|
+
export declare const conflictReport: (conflict: PushConflict, remote: string) => {
|
|
29
|
+
path: string;
|
|
30
|
+
url: string;
|
|
31
|
+
reason: string;
|
|
32
|
+
parts?: {
|
|
33
|
+
[x: string]: string;
|
|
34
|
+
last_sync: string;
|
|
35
|
+
local: string;
|
|
36
|
+
}[] | undefined;
|
|
37
|
+
local_changed_at?: string | undefined;
|
|
38
|
+
};
|
|
39
|
+
export interface MirrorSyncOptions<S> extends Pick<PushOptions<S>, "principal" | "recreate" | "guard" | "operationId" | "errorOf" | "signal"> {
|
|
40
|
+
mirrors: Mirror[];
|
|
41
|
+
target: DocumentTarget<S>;
|
|
42
|
+
stateOf(mirror: Mirror): PushStatePort<S>;
|
|
43
|
+
keep?: Keep;
|
|
44
|
+
/** Taken once for all the mirrors, so a second sync or a watcher's next cycle waits its turn. */
|
|
45
|
+
lock(): Promise<{
|
|
46
|
+
assertHeld(): void;
|
|
47
|
+
release(): Promise<void> | void;
|
|
48
|
+
}>;
|
|
49
|
+
/** How the app names the remote side in conflict reports. */
|
|
50
|
+
remote: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Syncs each mirror both ways. A mirror with conflicts, missing remote files or errors does not stop the others;
|
|
54
|
+
* it gets one `mirror.partial` issue, so the caller can answer `complete: false` and fail after printing.
|
|
55
|
+
*/
|
|
56
|
+
export declare const syncMirrors: <S>(options: MirrorSyncOptions<S>) => Promise<{
|
|
57
|
+
items: {
|
|
58
|
+
created: string[];
|
|
59
|
+
updated: string[];
|
|
60
|
+
pulled: string[];
|
|
61
|
+
unchanged: number;
|
|
62
|
+
missing: string[];
|
|
63
|
+
orphans: {
|
|
64
|
+
path: string;
|
|
65
|
+
url: string;
|
|
66
|
+
}[];
|
|
67
|
+
errors: {
|
|
68
|
+
path: string;
|
|
69
|
+
code: string;
|
|
70
|
+
message: string;
|
|
71
|
+
}[];
|
|
72
|
+
unsupported: number;
|
|
73
|
+
plan?: {
|
|
74
|
+
create: string[];
|
|
75
|
+
update: string[];
|
|
76
|
+
orphans: string[];
|
|
77
|
+
};
|
|
78
|
+
stopped?: true;
|
|
79
|
+
id: string;
|
|
80
|
+
path: string;
|
|
81
|
+
conflicts: {
|
|
82
|
+
path: string;
|
|
83
|
+
url: string;
|
|
84
|
+
reason: string;
|
|
85
|
+
parts?: {
|
|
86
|
+
[x: string]: string;
|
|
87
|
+
last_sync: string;
|
|
88
|
+
local: string;
|
|
89
|
+
}[] | undefined;
|
|
90
|
+
local_changed_at?: string | undefined;
|
|
91
|
+
}[];
|
|
92
|
+
}[];
|
|
93
|
+
issues: {
|
|
94
|
+
code: "provider_error";
|
|
95
|
+
reason: string;
|
|
96
|
+
source: string;
|
|
97
|
+
}[];
|
|
98
|
+
}>;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdirSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { CliError, writeSecurely } from "@wirecat/cli-base";
|
|
5
|
+
import * as v from "valibot";
|
|
6
|
+
import { planPush, pushDocuments, scanLocal } from "./push.js";
|
|
7
|
+
export const mirrorId = (root, folder) => createHash("sha256")
|
|
8
|
+
.update(JSON.stringify([root, folder]))
|
|
9
|
+
.digest("hex")
|
|
10
|
+
.slice(0, 8);
|
|
11
|
+
const ID = v.pipe(v.string(), v.regex(/^[A-Za-z0-9_-]{1,200}$/));
|
|
12
|
+
const stateSchema = (kinds) => v.object({
|
|
13
|
+
version: v.literal(1),
|
|
14
|
+
principal: v.string(),
|
|
15
|
+
root: v.string(),
|
|
16
|
+
target: ID,
|
|
17
|
+
folders: v.record(v.string(), ID),
|
|
18
|
+
entries: v.record(v.string(), v.object({
|
|
19
|
+
id: ID,
|
|
20
|
+
url: v.string(),
|
|
21
|
+
kind: v.string(),
|
|
22
|
+
hash: v.string(),
|
|
23
|
+
// Checked by kind only: the rest is the provider's own read-back.
|
|
24
|
+
snapshot: v.looseObject({ kind: v.picklist(kinds) }),
|
|
25
|
+
pushedAt: v.string(),
|
|
26
|
+
})),
|
|
27
|
+
pending: v.record(v.string(), v.object({ origin: v.string(), operationId: v.string() })),
|
|
28
|
+
});
|
|
29
|
+
/**
|
|
30
|
+
* A mirror's sync state in `<dir>/<id>.json`. A missing file is a first sync; an unreadable or invalid one is
|
|
31
|
+
* refused rather than reset, since a reset would push every file again as new.
|
|
32
|
+
*/
|
|
33
|
+
export const fileMirrorState = (dir, mirror, principal, kinds) => {
|
|
34
|
+
const path = join(dir, `${mirror.id}.json`);
|
|
35
|
+
const schema = stateSchema(kinds);
|
|
36
|
+
return {
|
|
37
|
+
read: () => {
|
|
38
|
+
let value;
|
|
39
|
+
try {
|
|
40
|
+
value = JSON.parse(readFileSync(path, "utf8"));
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (error.code === "ENOENT")
|
|
44
|
+
return {
|
|
45
|
+
version: 1,
|
|
46
|
+
principal,
|
|
47
|
+
root: mirror.path,
|
|
48
|
+
target: mirror.folder,
|
|
49
|
+
folders: {},
|
|
50
|
+
entries: {},
|
|
51
|
+
pending: {},
|
|
52
|
+
};
|
|
53
|
+
throw new CliError("configuration_error", "mirror state is unreadable; refusing to reset it", {
|
|
54
|
+
reason: "state.invalid",
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
const parsed = v.safeParse(schema, value);
|
|
58
|
+
if (!parsed.success)
|
|
59
|
+
throw new CliError("configuration_error", "mirror state is invalid; refusing to reset it", {
|
|
60
|
+
reason: "state.invalid",
|
|
61
|
+
});
|
|
62
|
+
return parsed.output;
|
|
63
|
+
},
|
|
64
|
+
save: (state) => {
|
|
65
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
66
|
+
writeSecurely(path, `${JSON.stringify(state)}\n`, 0o600);
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
/** What the next sync of each mirror would create or send, from local files and the state alone. */
|
|
71
|
+
export const mirrorStatus = async (mirrors, kindOf, stateOf) => {
|
|
72
|
+
const items = [];
|
|
73
|
+
for (const mirror of mirrors) {
|
|
74
|
+
const { files, unsupported } = await scanLocal(mirror.path, kindOf);
|
|
75
|
+
const plan = planPush(files, stateOf(mirror).read());
|
|
76
|
+
items.push({
|
|
77
|
+
id: mirror.id,
|
|
78
|
+
path: mirror.path,
|
|
79
|
+
folder: mirror.folder,
|
|
80
|
+
create: plan.create.map((file) => file.path),
|
|
81
|
+
update: plan.update.map((file) => file.path),
|
|
82
|
+
unchanged: plan.unchanged.length,
|
|
83
|
+
orphans: plan.orphans,
|
|
84
|
+
unsupported,
|
|
85
|
+
remote_checked: false,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return items;
|
|
89
|
+
};
|
|
90
|
+
/** A conflict as the user reads it, with the remote side named the way the app names it (`google`). */
|
|
91
|
+
export const conflictReport = (conflict, remote) => ({
|
|
92
|
+
path: conflict.path,
|
|
93
|
+
url: conflict.url,
|
|
94
|
+
reason: conflict.reason,
|
|
95
|
+
...(conflict.parts
|
|
96
|
+
? { parts: conflict.parts.map((part) => ({ last_sync: part.base, local: part.local, [remote]: part.remote })) }
|
|
97
|
+
: {}),
|
|
98
|
+
...(conflict.localChangedAt ? { local_changed_at: conflict.localChangedAt } : {}),
|
|
99
|
+
...(conflict.remoteChangedAt ? { [`${remote}_changed_at`]: conflict.remoteChangedAt } : {}),
|
|
100
|
+
...(conflict.remoteChangedBy ? { [`${remote}_changed_by`]: conflict.remoteChangedBy } : {}),
|
|
101
|
+
});
|
|
102
|
+
/**
|
|
103
|
+
* Syncs each mirror both ways. A mirror with conflicts, missing remote files or errors does not stop the others;
|
|
104
|
+
* it gets one `mirror.partial` issue, so the caller can answer `complete: false` and fail after printing.
|
|
105
|
+
*/
|
|
106
|
+
export const syncMirrors = async (options) => {
|
|
107
|
+
const { mirrors, keep, remote } = options;
|
|
108
|
+
const lock = await options.lock();
|
|
109
|
+
const results = [];
|
|
110
|
+
try {
|
|
111
|
+
for (const mirror of mirrors)
|
|
112
|
+
results.push({
|
|
113
|
+
id: mirror.id,
|
|
114
|
+
path: mirror.path,
|
|
115
|
+
...(await pushDocuments({
|
|
116
|
+
root: mirror.path,
|
|
117
|
+
folder: mirror.folder,
|
|
118
|
+
target: options.target,
|
|
119
|
+
state: options.stateOf(mirror),
|
|
120
|
+
principal: options.principal,
|
|
121
|
+
...(keep ? { keep } : {}),
|
|
122
|
+
recreate: options.recreate === true,
|
|
123
|
+
guard: options.guard,
|
|
124
|
+
operationId: options.operationId,
|
|
125
|
+
errorOf: options.errorOf,
|
|
126
|
+
assertHeld: () => lock.assertHeld(),
|
|
127
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
128
|
+
})),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
await lock.release();
|
|
133
|
+
}
|
|
134
|
+
const issues = results
|
|
135
|
+
.filter((result) => result.errors.length || result.conflicts.length || result.missing.length)
|
|
136
|
+
.map((result) => ({ code: "provider_error", reason: "mirror.partial", source: `mirror ${result.id}` }));
|
|
137
|
+
return {
|
|
138
|
+
items: results.map(({ conflicts, ...result }) => ({
|
|
139
|
+
...result,
|
|
140
|
+
conflicts: conflicts.map((conflict) => conflictReport(conflict, remote)),
|
|
141
|
+
})),
|
|
142
|
+
issues,
|
|
143
|
+
};
|
|
144
|
+
};
|
|
@@ -1,22 +1,7 @@
|
|
|
1
|
-
import type { MeetingChatLinesInput, MeetingHitSearchRequest } from "../meetings/index.js";
|
|
1
|
+
import type { MeetingChatLinesInput, MeetingHitSearchRequest, MeetingLink } from "../meetings/index.js";
|
|
2
2
|
import type { MessageStore } from "../store/store.js";
|
|
3
|
+
export type { MeetingLink };
|
|
3
4
|
export type MeetingLinksRequest = Omit<MeetingHitSearchRequest, "limit" | "after" | "maxReadBytes"> & Pick<MeetingChatLinesInput, "maxMessages">;
|
|
4
|
-
/** One link as the meetings' chat holds it: where and how often it was shared. */
|
|
5
|
-
export interface MeetingLink {
|
|
6
|
-
url: string;
|
|
7
|
-
domain: string | null;
|
|
8
|
-
/** The Google Docs, Sheets, Slides, Forms or Drive file it names. */
|
|
9
|
-
documentId: string | null;
|
|
10
|
-
count: number;
|
|
11
|
-
firstAt: number;
|
|
12
|
-
lastAt: number;
|
|
13
|
-
senders: string[];
|
|
14
|
-
meetings: {
|
|
15
|
-
meetingId: number;
|
|
16
|
-
accountId: number;
|
|
17
|
-
title: string | null;
|
|
18
|
-
}[];
|
|
19
|
-
}
|
|
20
5
|
/** Links shared in the chat of the selected meetings, most shared first; found when read, not stored. */
|
|
21
6
|
export declare const readMeetingLinks: (store: Pick<MessageStore, "meetings"> & {
|
|
22
7
|
knowledge?: MessageStore["knowledge"];
|
|
@@ -283,6 +283,7 @@ export const messagesService = (deps) => {
|
|
|
283
283
|
sendId: id,
|
|
284
284
|
operationId: id,
|
|
285
285
|
length: text.length,
|
|
286
|
+
...(attachments.length > 1 ? { count: attachments.length } : {}),
|
|
286
287
|
...(replyTo === undefined ? {} : { replyTo }),
|
|
287
288
|
...(threadId === undefined ? {} : { threadId }),
|
|
288
289
|
...(sendAs === undefined ? {} : { sendAs }),
|
|
@@ -4,6 +4,7 @@ import { formatMeetingReference } from "../meetings/index.js";
|
|
|
4
4
|
import { entriesOfList, fitting } from "../search/account-list.js";
|
|
5
5
|
import { SEARCH_QUERY } from "../search/lucene/types.js";
|
|
6
6
|
import { codeOf } from "../sends/guarded.js";
|
|
7
|
+
import { MAX_SEARCH_OFFSET } from "./messages-archive-search.js";
|
|
7
8
|
import { RRF_K } from "./notes-search.js";
|
|
8
9
|
import { RESOURCES_SEARCHED, searchAll, } from "./search-all.js";
|
|
9
10
|
import { listContextOf } from "./search-kind.js";
|
|
@@ -139,26 +140,35 @@ export const searchAllIncludingMeetings = async (store, account, request, messen
|
|
|
139
140
|
whole(request.meetingCursor.hitOffset, 0, Number.MAX_SAFE_INTEGER, "cursor hit offset");
|
|
140
141
|
}
|
|
141
142
|
cancelled(request.signal);
|
|
142
|
-
const
|
|
143
|
+
const others = { ...request, only: only.filter((one) => one !== "meetings") };
|
|
144
|
+
if (!only.includes("meetings")) {
|
|
145
|
+
const found = await searchAll(store, account, others, messenger, searchMessages);
|
|
146
|
+
cancelled(request.signal);
|
|
147
|
+
return found;
|
|
148
|
+
}
|
|
149
|
+
// Meetings rank with the rest, so a later page is the same merge read deeper, then cut.
|
|
150
|
+
const offset = request.offset ?? 0;
|
|
151
|
+
const window = offset + request.limit;
|
|
152
|
+
const legacy = await searchAll(store, account, { ...others, offset: 0, limit: window }, messenger, searchMessages);
|
|
143
153
|
cancelled(request.signal);
|
|
144
|
-
if (!only.includes("meetings"))
|
|
145
|
-
return legacy;
|
|
146
154
|
const compiled = meetingQuery(request.text, request.exact);
|
|
147
155
|
if ("reason" in compiled)
|
|
148
156
|
return {
|
|
149
157
|
...legacy,
|
|
158
|
+
items: legacy.items.slice(offset),
|
|
150
159
|
skipped: [...legacy.skipped, { resource: "meetings", code: "validation_error", reason: compiled.reason }],
|
|
151
160
|
};
|
|
152
161
|
let meetings;
|
|
153
162
|
const meetingAccount = request.meetingAccount ?? account;
|
|
154
163
|
try {
|
|
155
|
-
meetings = await collectMeetings(store, meetingAccount, request, compiled.query);
|
|
164
|
+
meetings = await collectMeetings(store, meetingAccount, { ...request, limit: window }, compiled.query);
|
|
156
165
|
}
|
|
157
166
|
catch (error) {
|
|
158
167
|
if (codeOf(error) !== "not_found")
|
|
159
168
|
throw error;
|
|
160
169
|
return {
|
|
161
170
|
...legacy,
|
|
171
|
+
items: legacy.items.slice(offset),
|
|
162
172
|
skipped: [
|
|
163
173
|
...legacy.skipped,
|
|
164
174
|
{ resource: "meetings", code: "not_found", reason: "the selected meeting account is not stored" },
|
|
@@ -176,13 +186,16 @@ export const searchAllIncludingMeetings = async (store, account, request, messen
|
|
|
176
186
|
ranked.set(key, held);
|
|
177
187
|
});
|
|
178
188
|
const ordered = [...ranked.values()].sort((a, b) => b.score - a.score).map(({ item }) => item);
|
|
179
|
-
const items = ordered.slice(
|
|
180
|
-
const returnedMeetings =
|
|
189
|
+
const items = ordered.slice(offset, window);
|
|
190
|
+
const returnedMeetings = ordered.slice(0, window).filter((item) => item.kind === "meeting").length;
|
|
181
191
|
const nextCursor = meetings.items[returnedMeetings]?.before ?? (!meetings.coverage.complete ? meetings.after : undefined);
|
|
192
|
+
const more = legacy.hasMore || ordered.length > window;
|
|
193
|
+
const { nextOffset: _, ...rest } = legacy;
|
|
182
194
|
return {
|
|
183
|
-
...
|
|
195
|
+
...rest,
|
|
184
196
|
items,
|
|
185
|
-
hasMore:
|
|
197
|
+
hasMore: more ? true : meetings.coverage.complete ? false : null,
|
|
198
|
+
...(more && window <= MAX_SEARCH_OFFSET ? { nextOffset: window } : {}),
|
|
186
199
|
searched: [...legacy.searched, "meetings"],
|
|
187
200
|
accounts: [
|
|
188
201
|
...legacy.accounts,
|
|
@@ -5,7 +5,7 @@ import { type TaskCategory } from "./task-categories.js";
|
|
|
5
5
|
export interface RuleResult {
|
|
6
6
|
added: number;
|
|
7
7
|
closed: number;
|
|
8
|
-
/** Opened tasks that may be the same job as another: on the review list (`tasks duplicates`). */
|
|
8
|
+
/** Opened tasks that may be the same job as another: on the review list (`tasks duplicates list`). */
|
|
9
9
|
review: number;
|
|
10
10
|
/** Tasks the rules found but did not open: over the per-chat limit, or a dry run. */
|
|
11
11
|
held?: number;
|
package/dist/services/tasks.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export declare const TASK_QUERY: {
|
|
|
26
26
|
readonly backend: "sql";
|
|
27
27
|
readonly fields: import("@wirecat/cli-base/query").FieldSpec[];
|
|
28
28
|
};
|
|
29
|
-
/** Which pairs `tasks duplicates` lists: those waiting for the reviewing agent, or those it left to the owner. */
|
|
29
|
+
/** Which pairs `tasks duplicates list` lists: those waiting for the reviewing agent, or those it left to the owner. */
|
|
30
30
|
export declare const PAIR_DECISIONS: readonly ["review", "unsure"];
|
|
31
31
|
export type PairDecision = (typeof PAIR_DECISIONS)[number];
|
|
32
32
|
export declare const TASK_PAIR_QUERY: {
|
|
@@ -168,7 +168,7 @@ export interface TasksService {
|
|
|
168
168
|
/**
|
|
169
169
|
* Links a task to stored things (`msg:`, `chat:`, `person:`, `document:`…) or, for what the store does
|
|
170
170
|
* not hold, to the text as written — a URL, a message not saved yet. After a `msg:` target, `review` counts the
|
|
171
|
-
* pairs the duplicate check sent to `tasks duplicates`.
|
|
171
|
+
* pairs the duplicate check sent to `tasks duplicates list`.
|
|
172
172
|
*/
|
|
173
173
|
link(id: string, targets: {
|
|
174
174
|
references?: readonly string[];
|
package/dist/services/tasks.js
CHANGED
|
@@ -49,7 +49,7 @@ export const TASK_QUERY = {
|
|
|
49
49
|
},
|
|
50
50
|
}),
|
|
51
51
|
};
|
|
52
|
-
/** Which pairs `tasks duplicates` lists: those waiting for the reviewing agent, or those it left to the owner. */
|
|
52
|
+
/** Which pairs `tasks duplicates list` lists: those waiting for the reviewing agent, or those it left to the owner. */
|
|
53
53
|
export const PAIR_DECISIONS = ["review", "unsure"];
|
|
54
54
|
export const TASK_PAIR_QUERY = {
|
|
55
55
|
entity: "task_pair",
|
package/dist/store/exports.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type { AdminStoreRequest, AdminStoreResult } from "./sqlite/admin-statist
|
|
|
17
17
|
export { type AttachmentTextEntry, type AttachmentView, attachmentColumns, type FileAttachment, resetAttachmentWords, type TextOrigin, } from "./sqlite/attachment-texts.js";
|
|
18
18
|
export { pendingNormalization } from "./sqlite/backfill.js";
|
|
19
19
|
export type { ChatRekey, ChatRekeys } from "./sqlite/chat-rekey.js";
|
|
20
|
-
export { CHAT_COLUMNS, titlePart } from "./sqlite/chats.js";
|
|
20
|
+
export { CHAT_COLUMNS, TRACKED_CHAT_COLUMNS, titlePart } from "./sqlite/chats.js";
|
|
21
21
|
export { CHAT_LIST_KEY, type ChatCompleteness, fetchedKey, historyStartKey } from "./sqlite/completeness.js";
|
|
22
22
|
export { CONVERSATION_COLUMNS } from "./sqlite/conversations.js";
|
|
23
23
|
export type { CounterTarget } from "./sqlite/counters.js";
|
package/dist/store/exports.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { accountGroupsOf } from "./sqlite/account-groups.js";
|
|
2
2
|
export { attachmentColumns, resetAttachmentWords, } from "./sqlite/attachment-texts.js";
|
|
3
3
|
export { pendingNormalization } from "./sqlite/backfill.js";
|
|
4
|
-
export { CHAT_COLUMNS, titlePart } from "./sqlite/chats.js";
|
|
4
|
+
export { CHAT_COLUMNS, TRACKED_CHAT_COLUMNS, titlePart } from "./sqlite/chats.js";
|
|
5
5
|
export { CHAT_LIST_KEY, fetchedKey, historyStartKey } from "./sqlite/completeness.js";
|
|
6
6
|
export { CONVERSATION_COLUMNS } from "./sqlite/conversations.js";
|
|
7
7
|
export { messageOfEmail, threadConversation } from "./sqlite/emails.js";
|
package/dist/store/messages.d.ts
CHANGED
|
@@ -128,7 +128,9 @@ export interface AccountMessages {
|
|
|
128
128
|
/** Keeps a finished transcript; an empty one is not kept, so the message is heard again. */
|
|
129
129
|
keepTranscript(key: AccountKey, chatId: Id, messageId: Id, text: string, source: string): Promise<void>;
|
|
130
130
|
/** The chat's messages that may hold a Google document link, newest first, with the documents linked from each. */
|
|
131
|
-
documentLinkMessages(key: AccountKey, chatId: Id
|
|
131
|
+
documentLinkMessages(key: AccountKey, chatId: Id, options?: {
|
|
132
|
+
messageId?: Id;
|
|
133
|
+
}): Promise<DocumentLinkMessage[]>;
|
|
132
134
|
/** Messages of the account that name this person, newest first. */
|
|
133
135
|
mentioning(key: AccountKey, person: {
|
|
134
136
|
id: Id;
|
package/dist/store/messages.js
CHANGED
|
@@ -123,9 +123,9 @@ export const accountMessagesOver = (context) => {
|
|
|
123
123
|
return;
|
|
124
124
|
write(database, () => transcripts.keepTranscript(context, chatPkFor(accountPk(key), chatId), messageId, text, source));
|
|
125
125
|
},
|
|
126
|
-
documentLinkMessages: async (key, chatId) => {
|
|
126
|
+
documentLinkMessages: async (key, chatId, { messageId } = {}) => {
|
|
127
127
|
const chatKey = chatKeyOf(key, chatId);
|
|
128
|
-
return chatKey === undefined ? [] : documentLinkMessages(context, chatKey);
|
|
128
|
+
return chatKey === undefined ? [] : documentLinkMessages(context, chatKey, messageId);
|
|
129
129
|
},
|
|
130
130
|
mentioning: async (key, person, limit) => {
|
|
131
131
|
const accountKey = findAccountPk(key);
|
package/dist/store/roster.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ChatEvent, Id, Member } from "../domain/models.js";
|
|
2
2
|
import type { RetentionOptions } from "../domain/retention.js";
|
|
3
3
|
import type { StoreContext } from "./sqlite/open.js";
|
|
4
|
+
import type { Where } from "./sqlite/query-filter.js";
|
|
4
5
|
import { type RetentionResult } from "./sqlite/retention.js";
|
|
5
6
|
import type { MemberCount, MemberStay, ProfileRevision, RosterChange, RosterRead, TrackedChat } from "./sqlite/roster.js";
|
|
6
7
|
import type { AccountKey } from "./types.js";
|
|
@@ -35,7 +36,9 @@ export interface RosterStore {
|
|
|
35
36
|
}): Promise<ChatEvent[]>;
|
|
36
37
|
/** Starts or stops `serve`'s daily fetch of the chat's members; history already kept stays. */
|
|
37
38
|
trackMembers(key: AccountKey, chatId: Id, tracked: boolean): Promise<void>;
|
|
38
|
-
trackedChats(key: AccountKey
|
|
39
|
+
trackedChats(key: AccountKey, options?: {
|
|
40
|
+
where?: Where;
|
|
41
|
+
}): Promise<TrackedChat[]>;
|
|
39
42
|
retention(key: AccountKey, chatId: Id, options: RetentionOptions, evidence?: {
|
|
40
43
|
cohort: string;
|
|
41
44
|
cursor?: string;
|
package/dist/store/roster.js
CHANGED
|
@@ -52,9 +52,9 @@ export const rosterStoreOver = (context) => {
|
|
|
52
52
|
}));
|
|
53
53
|
},
|
|
54
54
|
trackMembers: async (key, chatId, tracked) => write(database, () => roster.setTracked(context, chatPkFor(accountPk(key), chatId), tracked)),
|
|
55
|
-
trackedChats: async (key) => {
|
|
55
|
+
trackedChats: async (key, { where } = {}) => {
|
|
56
56
|
const accountKey = findAccountPk(key);
|
|
57
|
-
return accountKey === undefined ? [] : roster.trackedChats(context, accountKey);
|
|
57
|
+
return accountKey === undefined ? [] : roster.trackedChats(context, accountKey, where);
|
|
58
58
|
},
|
|
59
59
|
retention: async (key, chatId, options, evidence) => {
|
|
60
60
|
const accountKey = findAccountPk(key);
|
|
@@ -94,6 +94,18 @@ export declare const CHAT_COLUMNS: {
|
|
|
94
94
|
readonly sql: "chats.participants_count";
|
|
95
95
|
};
|
|
96
96
|
};
|
|
97
|
+
export declare const TRACKED_CHAT_COLUMNS: {
|
|
98
|
+
readonly chat_id: {
|
|
99
|
+
readonly type: "kw";
|
|
100
|
+
readonly sql: "chats.external_id";
|
|
101
|
+
};
|
|
102
|
+
readonly title: {
|
|
103
|
+
readonly where: (node: import("@wirecat/cli-base/query").Predicate) => {
|
|
104
|
+
sql: string;
|
|
105
|
+
params: string[];
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
};
|
|
97
109
|
/** `title:*book*` and `title:book` ask the same: the title holds "book". */
|
|
98
110
|
export declare const titlePart: (value: string) => string;
|
|
99
111
|
export declare const listChats: ({ orm, now }: StoreContext, accountKey: number, { limit, offset, ...filter }: {
|
|
@@ -340,6 +340,7 @@ export const CHAT_COLUMNS = {
|
|
|
340
340
|
last_message_at: { type: "time", sql: "chats.last_message_at" },
|
|
341
341
|
participants_count: { type: "num", sql: "chats.participants_count" },
|
|
342
342
|
};
|
|
343
|
+
export const TRACKED_CHAT_COLUMNS = { chat_id: CHAT_COLUMNS.id, title: CHAT_COLUMNS.title };
|
|
343
344
|
/** `title:*book*` and `title:book` ask the same: the title holds "book". */
|
|
344
345
|
export const titlePart = (value) => value.replace(/^\*+|\*+$/g, "").trim();
|
|
345
346
|
export const listChats = ({ orm, now }, accountKey, { limit, offset = 0, ...filter }) => {
|
|
@@ -14,4 +14,4 @@ export interface DocumentLinkMessage {
|
|
|
14
14
|
}[];
|
|
15
15
|
}
|
|
16
16
|
/** Newest first. Read from the text, so an edit that removed a link drops it whatever link rows remain. */
|
|
17
|
-
export declare const documentLinkMessages: ({ database }: StoreContext, chatKey: number) => DocumentLinkMessage[];
|
|
17
|
+
export declare const documentLinkMessages: ({ database }: StoreContext, chatKey: number, messageId?: Id) => DocumentLinkMessage[];
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { linkKind } from "./link-kinds.js";
|
|
2
2
|
/** Newest first. Read from the text, so an edit that removed a link drops it whatever link rows remain. */
|
|
3
|
-
export const documentLinkMessages = ({ database }, chatKey) => {
|
|
4
|
-
const
|
|
5
|
-
.prepare("SELECT m.id, m.external_id, m.text, m.sent_at, m.sender_name, " +
|
|
3
|
+
export const documentLinkMessages = ({ database }, chatKey, messageId) => {
|
|
4
|
+
const read = database.prepare("SELECT m.id, m.external_id, m.text, m.sent_at, m.sender_name, " +
|
|
6
5
|
"coalesce(m.sender_chat_external_id, i.external_id) AS sender_id FROM messages m " +
|
|
7
6
|
"LEFT JOIN identities i ON i.id = m.sender_identity_id " +
|
|
8
7
|
"WHERE m.chat_id = ? AND m.deleted_at IS NULL AND (m.text LIKE '%google.com%' OR m.text LIKE '%forms.gle%') " +
|
|
9
|
-
"
|
|
10
|
-
.
|
|
8
|
+
(messageId === undefined ? "" : "AND m.external_id = ? ") +
|
|
9
|
+
"ORDER BY m.sent_at DESC, m.id DESC");
|
|
10
|
+
const rows = messageId === undefined ? read.all(chatKey) : read.all(chatKey, messageId);
|
|
11
11
|
const linked = database.prepare("SELECT json_extract(d.front_matter, '$.fileId') AS file_id, d.title FROM links l " +
|
|
12
12
|
"JOIN documents d ON d.id = l.to_id AND d.deleted_at IS NULL " +
|
|
13
13
|
`WHERE l.from_type = 'message' AND l.from_id = ? AND l.to_type = 'document' AND l.kind = ${linkKind("links-to")}`);
|
|
@@ -10,9 +10,9 @@ import {
|
|
|
10
10
|
entityKind,
|
|
11
11
|
jitCompatCheck,
|
|
12
12
|
sql
|
|
13
|
-
} from "./shared-
|
|
13
|
+
} from "./shared-HKXF6HKX.js";
|
|
14
14
|
|
|
15
|
-
// node_modules/.pnpm/drizzle-orm@1.0.0-rc.4_valibot@1.5.0_typescript@7.0.2__zod@4.6.
|
|
15
|
+
// node_modules/.pnpm/drizzle-orm@1.0.0-rc.4_valibot@1.5.0_typescript@7.0.2__zod@4.6.7/node_modules/drizzle-orm/bun-sqlite/session.js
|
|
16
16
|
var SQLiteBunSession = class extends SQLiteAsyncSession {
|
|
17
17
|
static [entityKind] = "SQLiteBunSession";
|
|
18
18
|
logger;
|
|
@@ -76,7 +76,7 @@ var SQLiteBunTransaction = class SQLiteBunTransaction2 extends SQLiteAsyncTransa
|
|
|
76
76
|
}
|
|
77
77
|
};
|
|
78
78
|
|
|
79
|
-
// node_modules/.pnpm/drizzle-orm@1.0.0-rc.4_valibot@1.5.0_typescript@7.0.2__zod@4.6.
|
|
79
|
+
// node_modules/.pnpm/drizzle-orm@1.0.0-rc.4_valibot@1.5.0_typescript@7.0.2__zod@4.6.7/node_modules/drizzle-orm/bun-sqlite/driver.js
|
|
80
80
|
import { Database } from "bun:sqlite";
|
|
81
81
|
var SQLiteBunDatabase = class extends SQLiteAsyncDatabase {
|
|
82
82
|
static [entityKind] = "SQLiteBunDatabase";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../../../node_modules/.pnpm/drizzle-orm@1.0.0-rc.4_valibot@1.5.0_typescript@7.0.2__zod@4.6.
|
|
3
|
+
"sources": ["../../../../node_modules/.pnpm/drizzle-orm@1.0.0-rc.4_valibot@1.5.0_typescript@7.0.2__zod@4.6.7/node_modules/src/bun-sqlite/session.ts", "../../../../node_modules/.pnpm/drizzle-orm@1.0.0-rc.4_valibot@1.5.0_typescript@7.0.2__zod@4.6.7/node_modules/src/bun-sqlite/driver.ts"],
|
|
4
4
|
"sourcesContent": ["/// <reference types=\"bun-types\" />\n\nimport type { Changes, Database } from 'bun:sqlite';\nimport { entityKind } from '~/entity.ts';\nimport { DrizzleQueryError } from '~/errors.ts';\nimport type { Logger } from '~/logger.ts';\nimport { NoopLogger } from '~/logger.ts';\nimport type { AnyRelations } from '~/relations.ts';\nimport { type Query, sql } from '~/sql/sql.ts';\nimport {\n\tSQLiteAsyncPreparedQuery,\n\ttype SQLiteAsyncPreparedQueryConfig as PreparedQueryConfigBase,\n\tSQLiteAsyncSession,\n\tSQLiteAsyncTransaction,\n\ttype SQLiteQueryExecutors,\n} from '~/sqlite-core/async/session.ts';\nimport type { SQLiteDialect } from '~/sqlite-core/dialect.ts';\nimport type { SQLiteExecuteMethod, SQLiteTransactionConfig } from '~/sqlite-core/session.ts';\nimport type { DrizzleTypeError } from '~/utils.ts';\n\nexport interface SQLiteBunSessionOptions {\n\tlogger?: Logger;\n\tcache?: Cache;\n}\n\nexport type SQLiteBunRunResult = Changes;\n\ntype PreparedQueryConfig = Omit<PreparedQueryConfigBase, 'statement' | 'run'>;\n\nexport class SQLiteBunSession<TRelations extends AnyRelations>\n\textends SQLiteAsyncSession<'sync', SQLiteBunRunResult, TRelations>\n{\n\tstatic override readonly [entityKind]: string = 'SQLiteBunSession';\n\n\tprivate logger: Logger;\n\n\tconstructor(\n\t\tprivate client: Database,\n\t\tdialect: SQLiteDialect,\n\t\tprivate relations: TRelations,\n\t\tprivate options: SQLiteBunSessionOptions = {},\n\t) {\n\t\tsuper(dialect, 'sync');\n\t\tthis.logger = options.logger ?? new NoopLogger();\n\t}\n\n\texec(query: string): void {\n\t\tthis.client.exec(query);\n\t}\n\n\tprepareQuery<T extends Omit<PreparedQueryConfig, 'run'>>(\n\t\tquery: Query,\n\t\tmode: 'arrays' | 'objects' | 'raw',\n\t\t_prepare: boolean,\n\t\texecuteMethod?: SQLiteExecuteMethod,\n\t\tmapper?: (rows: any[]) => any,\n\t\tqueryMetadata?: {\n\t\t\ttype: 'select' | 'update' | 'delete' | 'insert';\n\t\t\ttables: string[];\n\t\t},\n\t): SQLiteAsyncPreparedQuery<T & { run: SQLiteBunRunResult }> {\n\t\tlet stmt: ReturnType<typeof this.client.query>;\n\t\ttry {\n\t\t\tstmt = this.client.query(query.sql);\n\t\t} catch (e) {\n\t\t\tthrow new DrizzleQueryError(query.sql, query.params, e as Error);\n\t\t}\n\t\tconst executors: SQLiteQueryExecutors<'sync'> = {\n\t\t\tall: (params) => {\n\t\t\t\tif (mode === 'arrays') return stmt.values(...params as any[]);\n\t\t\t\treturn stmt.all(...params as any[]);\n\t\t\t},\n\t\t\tget: (params) => {\n\t\t\t\tif (mode === 'arrays') return stmt.values(...params as any[])[0];\n\t\t\t\treturn stmt.get(...params as any[]);\n\t\t\t},\n\t\t\trun: (params) => {\n\t\t\t\treturn stmt.run(...params as any[]);\n\t\t\t},\n\t\t\tvalues: (params) => {\n\t\t\t\treturn stmt.values(...params as any[]);\n\t\t\t},\n\t\t};\n\n\t\treturn new SQLiteAsyncPreparedQuery(\n\t\t\t'sync',\n\t\t\texecuteMethod,\n\t\t\texecutors,\n\t\t\tquery,\n\t\t\tmapper,\n\t\t\tmode,\n\t\t\tthis.logger,\n\t\t\tundefined,\n\t\t\tqueryMetadata,\n\t\t\tundefined,\n\t\t);\n\t}\n\n\toverride transaction<T>(\n\t\ttransaction: (tx: SQLiteBunTransaction<TRelations>) => T,\n\t\tconfig: SQLiteTransactionConfig = {},\n\t): T {\n\t\tconst tx = new SQLiteBunTransaction('sync', this.dialect, this, this.relations);\n\t\tlet result: T | undefined;\n\t\tconst nativeTx = this.client.transaction(() => {\n\t\t\tresult = transaction(tx);\n\t\t});\n\t\tnativeTx[config.behavior ?? 'deferred']();\n\t\treturn result!;\n\t}\n}\n\nexport class SQLiteBunTransaction<\n\tTRelations extends AnyRelations,\n> extends SQLiteAsyncTransaction<'sync', SQLiteBunRunResult, TRelations> {\n\tstatic override readonly [entityKind]: string = 'SQLiteBunTransaction';\n\n\toverride transaction<T>(\n\t\ttransaction: (\n\t\t\ttx: SQLiteBunTransaction<TRelations>,\n\t\t) => T extends Promise<any> ? DrizzleTypeError<\"Sync drivers can't use async functions in transactions!\">\n\t\t\t: T,\n\t): T {\n\t\tconst savepointName = `sp${this.nestedIndex}`;\n\t\tconst tx = new SQLiteBunTransaction(\n\t\t\t'sync',\n\t\t\tthis.dialect,\n\t\t\tthis.session,\n\t\t\tthis._.relations,\n\t\t\tthis.nestedIndex + 1,\n\t\t);\n\t\tthis.session.run(sql.raw(`savepoint ${savepointName}`));\n\t\ttry {\n\t\t\tconst result = transaction(tx);\n\t\t\tthis.session.run(sql.raw(`release savepoint ${savepointName}`));\n\t\t\treturn result as T;\n\t\t} catch (err) {\n\t\t\tthis.session.run(sql.raw(`rollback to savepoint ${savepointName}`));\n\t\t\tthrow err;\n\t\t}\n\t}\n}\n", "/// <reference types=\"bun-types\" />\n\nimport { Database } from 'bun:sqlite';\nimport { entityKind } from '~/entity.ts';\nimport { DefaultLogger } from '~/logger.ts';\nimport type { AnyRelations, EmptyRelations } from '~/relations.ts';\nimport { SQLiteAsyncDatabase } from '~/sqlite-core/async/db.ts';\nimport { SQLiteDialect } from '~/sqlite-core/dialect.ts';\nimport type { DrizzleSQLiteConfig } from '~/sqlite-core/utils.ts';\nimport { jitCompatCheck } from '~/utils.ts';\nimport { type SQLiteBunRunResult, SQLiteBunSession } from './session.ts';\n\nexport class SQLiteBunDatabase<\n\tTRelations extends AnyRelations = EmptyRelations,\n> extends SQLiteAsyncDatabase<'sync', SQLiteBunRunResult, TRelations> {\n\tstatic override readonly [entityKind]: string = 'SQLiteBunDatabase';\n}\n\ntype DrizzleSqliteBunDatabaseOptions = {\n\t/**\n\t * Open the database as read-only (no write operations, no create).\n\t *\n\t * Equivalent to {@link constants.SQLITE_OPEN_READONLY}\n\t */\n\treadonly?: boolean;\n\t/**\n\t * Allow creating a new database\n\t *\n\t * Equivalent to {@link constants.SQLITE_OPEN_CREATE}\n\t */\n\tcreate?: boolean;\n\t/**\n\t * Open the database as read-write\n\t *\n\t * Equivalent to {@link constants.SQLITE_OPEN_READWRITE}\n\t */\n\treadwrite?: boolean;\n};\n\nexport type DrizzleBunSqliteDatabaseConfig =\n\t| ({\n\t\tsource?: string;\n\t} & DrizzleSqliteBunDatabaseOptions)\n\t| string\n\t| undefined;\n\nfunction construct<\n\tTRelations extends AnyRelations = EmptyRelations,\n>(\n\tclient: Database,\n\tconfig: DrizzleSQLiteConfig<TRelations> = {},\n): SQLiteBunDatabase<TRelations> & {\n\t$client: Database;\n} {\n\tconst dialect = new SQLiteDialect({\n\t\tuseJitMappers: jitCompatCheck(config.jit),\n\t});\n\tlet logger;\n\tif (config.logger === true) {\n\t\tlogger = new DefaultLogger();\n\t} else if (config.logger !== false) {\n\t\tlogger = config.logger;\n\t}\n\n\tconst relations = config.relations ?? {} as TRelations;\n\tconst session = new SQLiteBunSession<TRelations>(\n\t\tclient,\n\t\tdialect,\n\t\trelations,\n\t\t{\n\t\t\tlogger,\n\t\t},\n\t);\n\tconst db = new SQLiteBunDatabase('sync', dialect, session, relations);\n\t(<any> db).$client = client;\n\n\treturn db as any;\n}\n\nexport function drizzle<\n\tTRelations extends AnyRelations = EmptyRelations,\n\tTClient extends Database = Database,\n>(\n\t...params:\n\t\t| []\n\t\t| [\n\t\t\tstring,\n\t\t]\n\t\t| [\n\t\t\tstring,\n\t\t\tDrizzleSQLiteConfig<TRelations>,\n\t\t]\n\t\t| [\n\t\t\t(\n\t\t\t\t& DrizzleSQLiteConfig<TRelations>\n\t\t\t\t& ({\n\t\t\t\t\tconnection?: DrizzleBunSqliteDatabaseConfig;\n\t\t\t\t} | {\n\t\t\t\t\tclient: TClient;\n\t\t\t\t})\n\t\t\t),\n\t\t]\n): SQLiteBunDatabase<TRelations> & {\n\t$client: TClient;\n} {\n\tif (params[0] === undefined || typeof params[0] === 'string') {\n\t\tconst instance = params[0] === undefined ? new Database() : new Database(params[0]);\n\n\t\treturn construct(instance, params[1]) as any;\n\t}\n\n\tconst { connection, client, ...DrizzleSQLiteConfig } = params[0] as\n\t\t& ({\n\t\t\tconnection?: DrizzleBunSqliteDatabaseConfig | string;\n\t\t\tclient?: TClient;\n\t\t})\n\t\t& DrizzleSQLiteConfig<TRelations>;\n\n\tif (client) return construct(client, DrizzleSQLiteConfig) as any;\n\n\tif (typeof connection === 'object') {\n\t\tconst { source, ...opts } = connection;\n\n\t\tconst options = Object.values(opts).filter((v) => v !== undefined).length ? opts : undefined;\n\n\t\tconst instance = new Database(source, options);\n\n\t\treturn construct(instance, DrizzleSQLiteConfig) as any;\n\t}\n\n\tconst instance = new Database(connection);\n\n\treturn construct(instance, DrizzleSQLiteConfig) as any;\n}\n\nexport namespace drizzle {\n\texport function mock<TRelations extends AnyRelations = EmptyRelations>(\n\t\tconfig?: DrizzleSQLiteConfig<TRelations>,\n\t): SQLiteBunDatabase<TRelations> & {\n\t\t$client: '$client is not available on drizzle.mock()';\n\t} {\n\t\treturn construct({} as any, config) as any;\n\t}\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": ["SQLiteBunTransaction"]
|