@leemour/cli-memo 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +121 -2
- package/dist/app.d.ts +9 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +10 -0
- package/dist/app.js.map +1 -0
- package/dist/bin/memo.d.ts +3 -0
- package/dist/bin/memo.d.ts.map +1 -0
- package/dist/bin/memo.js +14 -0
- package/dist/bin/memo.js.map +1 -0
- package/dist/config.d.ts +25 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +20 -0
- package/dist/config.js.map +1 -0
- package/dist/context/command.d.ts +6 -0
- package/dist/context/command.d.ts.map +1 -0
- package/dist/context/command.js +56 -0
- package/dist/context/command.js.map +1 -0
- package/dist/context/context.d.ts +34 -0
- package/dist/context/context.d.ts.map +1 -0
- package/dist/context/context.js +50 -0
- package/dist/context/context.js.map +1 -0
- package/dist/mail/command.d.ts +4 -0
- package/dist/mail/command.d.ts.map +1 -0
- package/dist/mail/command.js +56 -0
- package/dist/mail/command.js.map +1 -0
- package/dist/mail/gmail.d.ts +14 -0
- package/dist/mail/gmail.d.ts.map +1 -0
- package/dist/mail/gmail.js +57 -0
- package/dist/mail/gmail.js.map +1 -0
- package/dist/mail/himalaya.d.ts +4 -0
- package/dist/mail/himalaya.d.ts.map +1 -0
- package/dist/mail/himalaya.js +13 -0
- package/dist/mail/himalaya.js.map +1 -0
- package/dist/mail/import.d.ts +29 -0
- package/dist/mail/import.d.ts.map +1 -0
- package/dist/mail/import.js +78 -0
- package/dist/mail/import.js.map +1 -0
- package/dist/mail/parse.d.ts +13 -0
- package/dist/mail/parse.d.ts.map +1 -0
- package/dist/mail/parse.js +53 -0
- package/dist/mail/parse.js.map +1 -0
- package/dist/notes/command.d.ts +4 -0
- package/dist/notes/command.d.ts.map +1 -0
- package/dist/notes/command.js +103 -0
- package/dist/notes/command.js.map +1 -0
- package/dist/notes/embed.d.ts +33 -0
- package/dist/notes/embed.d.ts.map +1 -0
- package/dist/notes/embed.js +74 -0
- package/dist/notes/embed.js.map +1 -0
- package/dist/notes/files.d.ts +8 -0
- package/dist/notes/files.d.ts.map +1 -0
- package/dist/notes/files.js +42 -0
- package/dist/notes/files.js.map +1 -0
- package/dist/notes/import.d.ts +24 -0
- package/dist/notes/import.d.ts.map +1 -0
- package/dist/notes/import.js +129 -0
- package/dist/notes/import.js.map +1 -0
- package/dist/notes/read.d.ts +31 -0
- package/dist/notes/read.d.ts.map +1 -0
- package/dist/notes/read.js +97 -0
- package/dist/notes/read.js.map +1 -0
- package/dist/notes/search.d.ts +43 -0
- package/dist/notes/search.d.ts.map +1 -0
- package/dist/notes/search.js +83 -0
- package/dist/notes/search.js.map +1 -0
- package/dist/notes/text.d.ts +3 -0
- package/dist/notes/text.d.ts.map +1 -0
- package/dist/notes/text.js +13 -0
- package/dist/notes/text.js.map +1 -0
- package/dist/options.d.ts +2 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +8 -0
- package/dist/options.js.map +1 -0
- package/dist/people/command.d.ts +5 -0
- package/dist/people/command.d.ts.map +1 -0
- package/dist/people/command.js +34 -0
- package/dist/people/command.js.map +1 -0
- package/dist/people/identity.d.ts +4 -0
- package/dist/people/identity.d.ts.map +1 -0
- package/dist/people/identity.js +13 -0
- package/dist/people/identity.js.map +1 -0
- package/dist/people/notes-map.d.ts +8 -0
- package/dist/people/notes-map.d.ts.map +1 -0
- package/dist/people/notes-map.js +8 -0
- package/dist/people/notes-map.js.map +1 -0
- package/dist/program.d.ts +7 -0
- package/dist/program.d.ts.map +1 -0
- package/dist/program.js +22 -0
- package/dist/program.js.map +1 -0
- package/dist/run/auto.d.ts +20 -0
- package/dist/run/auto.d.ts.map +1 -0
- package/dist/run/auto.js +139 -0
- package/dist/run/auto.js.map +1 -0
- package/dist/run/import.d.ts +18 -0
- package/dist/run/import.d.ts.map +1 -0
- package/dist/run/import.js +92 -0
- package/dist/run/import.js.map +1 -0
- package/dist/store/gone.d.ts +14 -0
- package/dist/store/gone.d.ts.map +1 -0
- package/dist/store/gone.js +48 -0
- package/dist/store/gone.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/package.json +53 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"himalaya.js","sourceRoot":"","sources":["../../src/mail/himalaya.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAI5C,qFAAqF;AACrF,MAAM,CAAC,MAAM,QAAQ,GACnB,CAAC,GAAG,GAAsB,OAAO,CAAC,GAAG,EAAY,EAAE,CACnD,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CACd,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IAC9B,MAAM,KAAK,GAAG,QAAQ,CACpB,GAAG,CAAC,aAAa,IAAI,UAAU,EAC/B,IAAI,EACJ,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,EACpC,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;QACxB,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,CAAC,MAAM,CAAC,CAAA;;YAEjC,MAAM,CACJ,IAAI,QAAQ,CAAC,gBAAgB,EAAE,YAAY,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,YAAY,MAAM,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,CACtG,CAAA;IACL,CAAC,CACF,CAAA;IACD,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAA;AAC/B,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { MessageStore } from "@leemour/cli-messaging/store";
|
|
2
|
+
import type { Himalaya } from "./himalaya.js";
|
|
3
|
+
export interface MailAccount {
|
|
4
|
+
/** The account's name in Himalaya's config. */
|
|
5
|
+
name: string;
|
|
6
|
+
/** The mailbox address: the store's account id. */
|
|
7
|
+
address: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ImportResult {
|
|
10
|
+
account: string;
|
|
11
|
+
since: string;
|
|
12
|
+
listed: number;
|
|
13
|
+
saved: number;
|
|
14
|
+
alreadyStored: number;
|
|
15
|
+
deleted: number;
|
|
16
|
+
/** False when `max` stopped the run before every new message was read; the next run continues. */
|
|
17
|
+
complete: boolean;
|
|
18
|
+
/** Why stored messages missing at the source were left alone, when they were. */
|
|
19
|
+
deletionsSkipped?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const importMail: ({ store, run, account, since, max, now, }: {
|
|
22
|
+
store: MessageStore;
|
|
23
|
+
run: Himalaya;
|
|
24
|
+
account: MailAccount;
|
|
25
|
+
since: Date;
|
|
26
|
+
max?: number;
|
|
27
|
+
now?: () => number;
|
|
28
|
+
}) => Promise<ImportResult>;
|
|
29
|
+
//# sourceMappingURL=import.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../src/mail/import.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAc,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAG5E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAG7C,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAA;IACZ,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,kGAAkG;IAClG,QAAQ,EAAE,OAAO,CAAA;IACjB,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAUD,eAAO,MAAM,UAAU,8CAOpB;IACD,KAAK,EAAE,YAAY,CAAA;IACnB,GAAG,EAAE,QAAQ,CAAA;IACb,OAAO,EAAE,WAAW,CAAA;IACpB,KAAK,EAAE,IAAI,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB,KAAG,OAAO,CAAC,YAAY,CAsEvB,CAAA"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { dropMissing } from "../store/gone.js";
|
|
2
|
+
import { listAllMail } from "./gmail.js";
|
|
3
|
+
import { parseMail } from "./parse.js";
|
|
4
|
+
const DAY = 86_400_000;
|
|
5
|
+
/** IMAP's SINCE counts whole days in the server's zone, so the window's first days are not trusted. */
|
|
6
|
+
const EDGE = 2 * DAY;
|
|
7
|
+
const ANY = /[\s\S]?/;
|
|
8
|
+
const titleOf = (subject) => subject?.replace(/^\s*((re|fwd?|aw|wg)\s*:\s*)+/i, "").trim() || null;
|
|
9
|
+
export const importMail = async ({ store, run, account, since, max = 200, now = Date.now, }) => {
|
|
10
|
+
const startedAt = now();
|
|
11
|
+
const key = { provider: "email", account: account.address.toLowerCase() };
|
|
12
|
+
await store.saveAccount(key, { name: account.address });
|
|
13
|
+
const listed = (await listAllMail(run, account.name, since)).sort((a, b) => b.receivedAt.localeCompare(a.receivedAt));
|
|
14
|
+
const present = new Set(listed.map(({ messageId }) => messageId));
|
|
15
|
+
let saved = 0;
|
|
16
|
+
let alreadyStored = 0;
|
|
17
|
+
let read = 0;
|
|
18
|
+
for (const item of listed) {
|
|
19
|
+
if ((await store.message(key, item.messageId, { chatId: item.threadId })) !== undefined) {
|
|
20
|
+
alreadyStored++;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (read >= max)
|
|
24
|
+
break;
|
|
25
|
+
read++;
|
|
26
|
+
const mail = parseMail(await run(["--json", "message", "read", "-a", account.name, "-m", "archive", item.uid]));
|
|
27
|
+
const people = [mail.from, ...mail.to].filter((person) => person !== null);
|
|
28
|
+
const known = (await store.members(key, item.threadId)).map(({ id }) => id);
|
|
29
|
+
const members = [...new Set([...known, ...people.map(({ email }) => email)])];
|
|
30
|
+
const newest = (await store.find({ account: key, chatId: item.threadId, pattern: ANY, limit: 1 })).items[0]
|
|
31
|
+
?.timestamp;
|
|
32
|
+
// Saving a chat replaces its fields, so an older message read later must not move the time back.
|
|
33
|
+
const lastMessageAt = newest !== undefined && newest > item.receivedAt ? newest : item.receivedAt;
|
|
34
|
+
await store.applyDelta(key, {
|
|
35
|
+
chats: [
|
|
36
|
+
{
|
|
37
|
+
id: item.threadId,
|
|
38
|
+
title: titleOf(mail.subject),
|
|
39
|
+
kind: members.length > 2 ? "group" : "dialog",
|
|
40
|
+
unreadCount: null,
|
|
41
|
+
lastMessageAt,
|
|
42
|
+
participantsCount: members.length,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
people: people.map(({ email, name }) => ({ id: email, name })),
|
|
46
|
+
members: new Map([[item.threadId, members]]),
|
|
47
|
+
});
|
|
48
|
+
const message = {
|
|
49
|
+
id: item.messageId,
|
|
50
|
+
chatId: item.threadId,
|
|
51
|
+
senderId: mail.from?.email ?? null,
|
|
52
|
+
senderName: mail.from?.name ?? null,
|
|
53
|
+
timestamp: item.receivedAt,
|
|
54
|
+
editedAt: null,
|
|
55
|
+
text: [mail.subject, mail.text].filter(Boolean).join("\n\n"),
|
|
56
|
+
outgoing: mail.from === null ? null : mail.from.email === key.account,
|
|
57
|
+
attachments: [],
|
|
58
|
+
replyTo: null,
|
|
59
|
+
forwardedFrom: null,
|
|
60
|
+
reactions: null,
|
|
61
|
+
};
|
|
62
|
+
await store.saveMessages(key, item.threadId, [message], { via: "himalaya", seenAt: startedAt });
|
|
63
|
+
saved++;
|
|
64
|
+
}
|
|
65
|
+
const complete = listed.length - alreadyStored - saved === 0;
|
|
66
|
+
const { deleted, skipped } = await dropMissing(store, key, present, new Date(since.getTime() + EDGE).toISOString());
|
|
67
|
+
return {
|
|
68
|
+
account: key.account,
|
|
69
|
+
since: since.toISOString(),
|
|
70
|
+
listed: listed.length,
|
|
71
|
+
saved,
|
|
72
|
+
alreadyStored,
|
|
73
|
+
deleted,
|
|
74
|
+
complete,
|
|
75
|
+
...(skipped === undefined ? {} : { deletionsSkipped: skipped }),
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import.js","sourceRoot":"","sources":["../../src/mail/import.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface Address {
|
|
2
|
+
email: string;
|
|
3
|
+
name: string | null;
|
|
4
|
+
}
|
|
5
|
+
export interface ParsedMail {
|
|
6
|
+
subject: string | null;
|
|
7
|
+
from: Address | null;
|
|
8
|
+
to: Address[];
|
|
9
|
+
text: string;
|
|
10
|
+
}
|
|
11
|
+
/** `himalaya --json message read`: the `mail-parser` crate's message, serialised as it is. */
|
|
12
|
+
export declare const parseMail: (json: string) => ParsedMail;
|
|
13
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/mail/parse.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;IACpB,EAAE,EAAE,OAAO,EAAE,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;CACb;AAiDD,8FAA8F;AAC9F,eAAO,MAAM,SAAS,SAAU,MAAM,KAAG,UAuBxC,CAAA"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { CliError } from "@leemour/cli-core";
|
|
2
|
+
const MAX_TEXT = 20_000;
|
|
3
|
+
const addresses = (value) => {
|
|
4
|
+
const address = value?.Address;
|
|
5
|
+
const list = address !== undefined && "List" in address
|
|
6
|
+
? (address.List ?? [])
|
|
7
|
+
: address !== undefined && "Group" in address
|
|
8
|
+
? (address.Group ?? []).flatMap((group) => group.addresses ?? [])
|
|
9
|
+
: [];
|
|
10
|
+
return list.flatMap(({ address: email, name }) => (email ? [{ email: email.toLowerCase(), name: name ?? null }] : []));
|
|
11
|
+
};
|
|
12
|
+
const bodyText = (part, kind) => {
|
|
13
|
+
const body = part?.body;
|
|
14
|
+
return typeof body === "object" && body !== null && typeof body[kind] === "string" ? body[kind] : "";
|
|
15
|
+
};
|
|
16
|
+
const htmlToText = (html) => html
|
|
17
|
+
.replace(/<(script|style)[\s\S]*?<\/\1>/gi, "")
|
|
18
|
+
.replace(/<br\s*\/?>|<\/(p|div|li|tr|h\d)>/gi, "\n")
|
|
19
|
+
.replace(/<[^>]+>/g, "")
|
|
20
|
+
.replace(/ /g, " ")
|
|
21
|
+
.replace(/&/g, "&")
|
|
22
|
+
.replace(/</g, "<")
|
|
23
|
+
.replace(/>/g, ">")
|
|
24
|
+
.replace(/"/g, '"')
|
|
25
|
+
.replace(/'/g, "'")
|
|
26
|
+
.replace(/[ \t]+\n/g, "\n")
|
|
27
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
28
|
+
.trim();
|
|
29
|
+
/** `himalaya --json message read`: the `mail-parser` crate's message, serialised as it is. */
|
|
30
|
+
export const parseMail = (json) => {
|
|
31
|
+
let parsed;
|
|
32
|
+
try {
|
|
33
|
+
parsed = JSON.parse(json);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
throw new CliError("invalid_response", "himalaya message read did not answer JSON");
|
|
37
|
+
}
|
|
38
|
+
const headers = parsed.parts[0]?.headers ?? [];
|
|
39
|
+
const header = (name) => headers.find((item) => item.name === name)?.value;
|
|
40
|
+
const subject = header("subject")?.Text ?? null;
|
|
41
|
+
const plain = parsed.text_body
|
|
42
|
+
.map((index) => bodyText(parsed.parts[index], "Text"))
|
|
43
|
+
.join("\n\n")
|
|
44
|
+
.trim();
|
|
45
|
+
const text = plain || htmlToText(parsed.html_body.map((index) => bodyText(parsed.parts[index], "Html")).join("\n"));
|
|
46
|
+
return {
|
|
47
|
+
subject,
|
|
48
|
+
from: addresses(header("from"))[0] ?? null,
|
|
49
|
+
to: [...addresses(header("to")), ...addresses(header("cc"))],
|
|
50
|
+
text: text.replace(/\r\n?/g, "\n").slice(0, MAX_TEXT),
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/mail/parse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/notes/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAE1E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA2CxC,eAAO,MAAM,YAAY,YAAa,OAAO,WAAW,OAAO,OAAO,MAAM,CAAC,UAAU,KAAG,IAyEzF,CAAA"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { CliError, createRenderer } from "@leemour/cli-core";
|
|
2
|
+
import { openStore } from "@leemour/cli-messaging/store";
|
|
3
|
+
import { loadConfig } from "../config.js";
|
|
4
|
+
import { positive } from "../options.js";
|
|
5
|
+
import { loadNotesMap } from "../people/notes-map.js";
|
|
6
|
+
import { embedChanged } from "./embed.js";
|
|
7
|
+
import { importNotes, notesKey } from "./import.js";
|
|
8
|
+
import { findNotes } from "./read.js";
|
|
9
|
+
import { searchNotes } from "./search.js";
|
|
10
|
+
import { notesText } from "./text.js";
|
|
11
|
+
const NO_FOLDERS = "no folders: pass --folder or set notes.folders in the config";
|
|
12
|
+
const searchText = (result) => [
|
|
13
|
+
...(result.hits.length === 0 ? [`No note holds "${result.query}". Run memo notes import if notes changed.`] : []),
|
|
14
|
+
...result.hits.map((hit) => `${hit.path}\n ${hit.line}`),
|
|
15
|
+
...(result.hasMore ? ["More notes match; raise --limit to see them."] : []),
|
|
16
|
+
...(result.linked.length === 0
|
|
17
|
+
? []
|
|
18
|
+
: ["", "Linked from these notes", ...result.linked.map(({ name, notes }) => ` ${name} (${notes})`)]),
|
|
19
|
+
...(result.mentioned.length === 0
|
|
20
|
+
? []
|
|
21
|
+
: [
|
|
22
|
+
"",
|
|
23
|
+
"Named in these notes (by name only — may be someone else)",
|
|
24
|
+
...result.mentioned.map(({ provider, id, name, notes }) => ` ${name} ${provider}:${id} (${notes})`),
|
|
25
|
+
]),
|
|
26
|
+
].join("\n");
|
|
27
|
+
const importText = (results) => results
|
|
28
|
+
.map((result) => `${result.folder}: ${result.notes} notes stored, ${result.deleted} gone from the folder` +
|
|
29
|
+
(result.deletionsSkipped === undefined ? "" : `\nDeletions skipped: ${result.deletionsSkipped}`))
|
|
30
|
+
.join("\n");
|
|
31
|
+
export const notesCommand = (program, streams, env) => {
|
|
32
|
+
const scope = (options) => {
|
|
33
|
+
const notes = loadConfig(env).notes;
|
|
34
|
+
return {
|
|
35
|
+
folders: options.folder ?? notes?.folders ?? [],
|
|
36
|
+
ignore: [...(notes?.ignore ?? []), ...(options.ignore ?? [])],
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const print = (json, value, text) => {
|
|
40
|
+
if (json)
|
|
41
|
+
createRenderer({ format: "json", color: false, streams }).result(value);
|
|
42
|
+
else
|
|
43
|
+
streams.data(`${text}\n`);
|
|
44
|
+
};
|
|
45
|
+
const notes = program.command("notes").description("Markdown notes: who they are about, and searching them");
|
|
46
|
+
const scoped = (command) => command
|
|
47
|
+
.option("--folder <path...>", "folders of notes (default: notes.folders in the config)")
|
|
48
|
+
.option("--ignore <path...>", "files or folders inside them to skip, added to notes.ignore — a path or a glob");
|
|
49
|
+
scoped(notes
|
|
50
|
+
.command("about")
|
|
51
|
+
.description("Notes about a person, notes that link them, and weak plain-name mentions; reads the folders")
|
|
52
|
+
.argument("<name>", "the person's name, as in the note's file name or its aliases"))
|
|
53
|
+
.option("--limit <n>", "most links and mentions to show of each kind", positive, 20)
|
|
54
|
+
.option("--json", "print JSON")
|
|
55
|
+
.action((name, options) => {
|
|
56
|
+
const { folders, ignore } = scope(options);
|
|
57
|
+
const answer = folders.length === 0
|
|
58
|
+
? { ...findNotes([], name), complete: false, notRead: [{ folder: "", reason: NO_FOLDERS }] }
|
|
59
|
+
: findNotes(folders, name, options.limit, ignore);
|
|
60
|
+
print(options.json, answer, notesText(answer));
|
|
61
|
+
});
|
|
62
|
+
scoped(notes.command("import").description("Load the notes into the shared store, so search finds them"))
|
|
63
|
+
.option("--no-embed", "skip embedding them for search by meaning")
|
|
64
|
+
.option("--json", "print JSON")
|
|
65
|
+
.action(async (options) => {
|
|
66
|
+
const { folders, ignore } = scope(options);
|
|
67
|
+
if (folders.length === 0)
|
|
68
|
+
throw new CliError("configuration_error", NO_FOLDERS);
|
|
69
|
+
const store = await openStore({ env });
|
|
70
|
+
try {
|
|
71
|
+
const results = [];
|
|
72
|
+
for (const folder of folders) {
|
|
73
|
+
const imported = await importNotes(store, folder, { ignore });
|
|
74
|
+
await embedChanged(store, notesKey(folder), imported.chats, {
|
|
75
|
+
embed: options.embed && loadConfig(env).notes?.embed !== false,
|
|
76
|
+
env,
|
|
77
|
+
});
|
|
78
|
+
results.push(imported);
|
|
79
|
+
}
|
|
80
|
+
print(options.json, results, importText(results));
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
await store.close();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
notes
|
|
87
|
+
.command("search")
|
|
88
|
+
.description("Find stored notes by their words, and who those notes link")
|
|
89
|
+
.argument("<text>", "words to find, three letters or more")
|
|
90
|
+
.option("--limit <n>", "most notes to show", positive, 20)
|
|
91
|
+
.option("--json", "print JSON")
|
|
92
|
+
.action(async (text, options) => {
|
|
93
|
+
const store = await openStore({ env });
|
|
94
|
+
try {
|
|
95
|
+
const result = await searchNotes(store, text, { limit: options.limit, notesMap: loadNotesMap(env) });
|
|
96
|
+
print(options.json, result, searchText(result));
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
await store.close();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../src/notes/command.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { AccountKey, MessageStore } from "@leemour/cli-messaging/store";
|
|
2
|
+
/** About three chunks a second with e5-small on this laptop (2026-10-06): a run stays near three minutes, under a five-minute timer, and the next resumes. */
|
|
3
|
+
export declare const EMBED_PER_RUN = 600;
|
|
4
|
+
export interface NotesEmbedded {
|
|
5
|
+
chats: number;
|
|
6
|
+
chunks: number;
|
|
7
|
+
/** Chunks left for the next run, when the run's bound stopped it. */
|
|
8
|
+
left: boolean;
|
|
9
|
+
/** Why nothing was embedded, when nothing could be. */
|
|
10
|
+
notEmbedded?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Builds each chat's conversations (one per note) — what search by words and by meaning both read — and,
|
|
14
|
+
* unless `embed` is off, embeds the chunks that have no vector yet. Chunks are
|
|
15
|
+
* keyed by their text's hash, so an unchanged note is never embedded twice. A missing model is reported,
|
|
16
|
+
* never downloaded.
|
|
17
|
+
*/
|
|
18
|
+
export declare const embedNotes: (store: MessageStore, key: AccountKey, chats: string[], { maxChunks, embed, env, }?: {
|
|
19
|
+
maxChunks?: number;
|
|
20
|
+
embed?: boolean;
|
|
21
|
+
env?: NodeJS.ProcessEnv;
|
|
22
|
+
}) => Promise<NotesEmbedded>;
|
|
23
|
+
/**
|
|
24
|
+
* Embeds what changed since the last run, and whatever an earlier run left. The folders waiting are kept in
|
|
25
|
+
* the store's sync state, so a run stopped by its bound, or a missing model, picks up next time; the first run
|
|
26
|
+
* after embedding exists takes every folder already stored.
|
|
27
|
+
*/
|
|
28
|
+
export declare const embedChanged: (store: MessageStore, key: AccountKey, changed: string[], options?: {
|
|
29
|
+
maxChunks?: number;
|
|
30
|
+
embed?: boolean;
|
|
31
|
+
env?: NodeJS.ProcessEnv;
|
|
32
|
+
}) => Promise<NotesEmbedded>;
|
|
33
|
+
//# sourceMappingURL=embed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embed.d.ts","sourceRoot":"","sources":["../../src/notes/embed.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAG5E,8JAA8J;AAC9J,eAAO,MAAM,aAAa,MAAM,CAAA;AAEhC,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,qEAAqE;IACrE,IAAI,EAAE,OAAO,CAAA;IACb,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAKD;;;;;GAKG;AACH,eAAO,MAAM,UAAU,UACd,YAAY,OACd,UAAU,SACR,MAAM,EAAE,+BAKZ;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,KAClE,OAAO,CAAC,aAAa,CAgCvB,CAAA;AAID;;;;GAIG;AACH,eAAO,MAAM,YAAY,UAChB,YAAY,OACd,UAAU,WACN,MAAM,EAAE,YACR;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,KACxE,OAAO,CAAC,aAAa,CAiBvB,CAAA"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { CliError } from "@leemour/cli-core";
|
|
2
|
+
import { conversationsService, embeddingsService, storeOnlyDeps } from "@leemour/cli-messaging/services";
|
|
3
|
+
import { APP } from "../app.js";
|
|
4
|
+
/** About three chunks a second with e5-small on this laptop (2026-10-06): a run stays near three minutes, under a five-minute timer, and the next resumes. */
|
|
5
|
+
export const EMBED_PER_RUN = 600;
|
|
6
|
+
const isMissingModel = (error) => error instanceof CliError && error.code === "not_found" && /is not downloaded/.test(error.message);
|
|
7
|
+
/**
|
|
8
|
+
* Builds each chat's conversations (one per note) — what search by words and by meaning both read — and,
|
|
9
|
+
* unless `embed` is off, embeds the chunks that have no vector yet. Chunks are
|
|
10
|
+
* keyed by their text's hash, so an unchanged note is never embedded twice. A missing model is reported,
|
|
11
|
+
* never downloaded.
|
|
12
|
+
*/
|
|
13
|
+
export const embedNotes = async (store, key, chats, { maxChunks = EMBED_PER_RUN, embed = true, env, } = {}) => {
|
|
14
|
+
const deps = storeOnlyDeps(store, key, { app: APP, ...(env === undefined ? {} : { env }) });
|
|
15
|
+
const conversations = conversationsService(deps);
|
|
16
|
+
const embeddings = embeddingsService(deps);
|
|
17
|
+
for (const chat of chats) {
|
|
18
|
+
await conversations.build(chat).catch((error) => {
|
|
19
|
+
// A folder whose every note was deleted has nothing left to build.
|
|
20
|
+
if (!(error instanceof CliError && error.code === "not_found"))
|
|
21
|
+
throw error;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
let chunks = 0;
|
|
25
|
+
let left = false;
|
|
26
|
+
for (const chat of embed ? chats : []) {
|
|
27
|
+
if (chunks >= maxChunks) {
|
|
28
|
+
left = true;
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const done = await embeddings.embed(chat, { maxChunks: maxChunks - chunks });
|
|
33
|
+
chunks += done.embedded;
|
|
34
|
+
if ((await embeddings.status(chat)).left > 0)
|
|
35
|
+
left = true;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (!isMissingModel(error))
|
|
39
|
+
throw error;
|
|
40
|
+
return {
|
|
41
|
+
chats: chats.length,
|
|
42
|
+
chunks,
|
|
43
|
+
left: true,
|
|
44
|
+
notEmbedded: "the e5-small model is not downloaded — tg models text download e5-small",
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { chats: chats.length, chunks, left, ...(embed ? {} : { notEmbedded: "embedding is off" }) };
|
|
49
|
+
};
|
|
50
|
+
const PENDING = "embed_pending";
|
|
51
|
+
/**
|
|
52
|
+
* Embeds what changed since the last run, and whatever an earlier run left. The folders waiting are kept in
|
|
53
|
+
* the store's sync state, so a run stopped by its bound, or a missing model, picks up next time; the first run
|
|
54
|
+
* after embedding exists takes every folder already stored.
|
|
55
|
+
*/
|
|
56
|
+
export const embedChanged = async (store, key, changed, options = {}) => {
|
|
57
|
+
const saved = await store.syncState(key, PENDING);
|
|
58
|
+
const waiting = saved === undefined
|
|
59
|
+
? (await store.chats(key, { limit: 10_000 })).items.map(({ id }) => id)
|
|
60
|
+
: JSON.parse(saved.value);
|
|
61
|
+
// A build under older rules, or one a change made stale, is rebuilt even when no note changed since.
|
|
62
|
+
const deps = storeOnlyDeps(store, key, { app: APP, ...(options.env === undefined ? {} : { env: options.env }) });
|
|
63
|
+
const behind = (await embeddingsService(deps).readiness({})).chats
|
|
64
|
+
.filter(({ state, graph, vectors }) => state === "stale" || graph?.outdatedRules === true || vectors.missing > 0)
|
|
65
|
+
.map(({ chat }) => chat);
|
|
66
|
+
const chats = [...new Set([...waiting, ...changed, ...behind])].sort();
|
|
67
|
+
if (chats.length === 0)
|
|
68
|
+
return { chats: 0, chunks: 0, left: false };
|
|
69
|
+
const result = await embedNotes(store, key, chats, options);
|
|
70
|
+
// Embedding off leaves every chat waiting, so turning it on later embeds them.
|
|
71
|
+
await store.setSyncState(key, PENDING, JSON.stringify(result.left || options.embed === false ? chats : []));
|
|
72
|
+
return result;
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=embed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embed.js","sourceRoot":"","sources":["../../src/notes/embed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const NOTE_EXTENSIONS: string[];
|
|
2
|
+
/**
|
|
3
|
+
* Note files under `folder`, as absolute paths. Hidden entries (`.obsidian`, `.trash`) are skipped, and
|
|
4
|
+
* so is whatever an `ignore` rule names — a path inside the folder (`Psychology/Personal Notes.md`,
|
|
5
|
+
* `Archive`) or a glob (`**\/Private*`, `*.txt`).
|
|
6
|
+
*/
|
|
7
|
+
export declare const noteFiles: (folder: string, ignore?: string[]) => string[];
|
|
8
|
+
//# sourceMappingURL=files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/notes/files.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,UAAkB,CAAA;AAoB9C;;;;GAIG;AACH,eAAO,MAAM,SAAS,WAAY,MAAM,WAAU,MAAM,EAAE,KAAQ,MAAM,EAevE,CAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { readdirSync } from "node:fs";
|
|
2
|
+
import { join, relative, sep } from "node:path";
|
|
3
|
+
export const NOTE_EXTENSIONS = [".md", ".txt"];
|
|
4
|
+
const pattern = (rule) => {
|
|
5
|
+
const glob = rule.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
|
|
6
|
+
const body = glob
|
|
7
|
+
.split(/(\*\*\/?|\*|\?)/)
|
|
8
|
+
.map((part) => part === "**/" || part === "**"
|
|
9
|
+
? ".*"
|
|
10
|
+
: part === "*"
|
|
11
|
+
? "[^/]*"
|
|
12
|
+
: part === "?"
|
|
13
|
+
? "[^/]"
|
|
14
|
+
: part.replace(/[.+^${}()|[\]\\]/g, "\\$&"))
|
|
15
|
+
.join("");
|
|
16
|
+
// A rule names a path inside the folder, and everything under it when it is a folder.
|
|
17
|
+
return new RegExp(`^${body}(/.*)?$`, "i");
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Note files under `folder`, as absolute paths. Hidden entries (`.obsidian`, `.trash`) are skipped, and
|
|
21
|
+
* so is whatever an `ignore` rule names — a path inside the folder (`Psychology/Personal Notes.md`,
|
|
22
|
+
* `Archive`) or a glob (`**\/Private*`, `*.txt`).
|
|
23
|
+
*/
|
|
24
|
+
export const noteFiles = (folder, ignore = []) => {
|
|
25
|
+
const rules = ignore.map(pattern);
|
|
26
|
+
const ignored = (path) => {
|
|
27
|
+
const inside = relative(folder, path).split(sep).join("/");
|
|
28
|
+
return rules.some((rule) => rule.test(inside));
|
|
29
|
+
};
|
|
30
|
+
const walk = (dir) => readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
31
|
+
if (entry.name.startsWith("."))
|
|
32
|
+
return [];
|
|
33
|
+
const path = join(dir, entry.name);
|
|
34
|
+
if (ignored(path))
|
|
35
|
+
return [];
|
|
36
|
+
if (entry.isDirectory())
|
|
37
|
+
return walk(path);
|
|
38
|
+
return entry.isFile() && NOTE_EXTENSIONS.some((ext) => entry.name.toLowerCase().endsWith(ext)) ? [path] : [];
|
|
39
|
+
});
|
|
40
|
+
return walk(folder);
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/notes/files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAE/C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;AAE9C,MAAM,OAAO,GAAG,CAAC,IAAY,EAAU,EAAE;IACvC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAA;IAC/D,MAAM,IAAI,GAAG,IAAI;SACd,KAAK,CAAC,iBAAiB,CAAC;SACxB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACZ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,IAAI;QAC7B,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,IAAI,KAAK,GAAG;YACZ,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,IAAI,KAAK,GAAG;gBACZ,CAAC,CAAC,MAAM;gBACR,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAClD;SACA,IAAI,CAAC,EAAE,CAAC,CAAA;IACX,sFAAsF;IACtF,OAAO,IAAI,MAAM,CAAC,IAAI,IAAI,SAAS,EAAE,GAAG,CAAC,CAAA;AAC3C,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAc,EAAE,MAAM,GAAa,EAAE,EAAY,EAAE;IAC3E,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACjC,MAAM,OAAO,GAAG,CAAC,IAAY,EAAE,EAAE;QAC/B,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC1D,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IAChD,CAAC,CAAA;IACD,MAAM,IAAI,GAAG,CAAC,GAAW,EAAY,EAAE,CACrC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QAC1D,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,CAAA;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QAClC,IAAI,OAAO,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAA;QAC5B,IAAI,KAAK,CAAC,WAAW,EAAE;YAAE,OAAO,IAAI,CAAC,IAAI,CAAC,CAAA;QAC1C,OAAO,KAAK,CAAC,MAAM,EAAE,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAC9G,CAAC,CAAC,CAAA;IACJ,OAAO,IAAI,CAAC,MAAM,CAAC,CAAA;AACrB,CAAC,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AccountKey, MessageStore } from "@leemour/cli-messaging/store";
|
|
2
|
+
export interface NotesImport {
|
|
3
|
+
folder: string;
|
|
4
|
+
notes: number;
|
|
5
|
+
/** Saved because they are new or their text changed. */
|
|
6
|
+
changed: number;
|
|
7
|
+
/** Chats (folders) a note was saved or deleted in: what needs building and embedding again. */
|
|
8
|
+
chats: string[];
|
|
9
|
+
deleted: number;
|
|
10
|
+
deletionsSkipped?: string;
|
|
11
|
+
}
|
|
12
|
+
/** The store's account for a folder of notes: its absolute path, so two vaults never mix. */
|
|
13
|
+
export declare const notesKey: (folder: string) => AccountKey;
|
|
14
|
+
/**
|
|
15
|
+
* Each note is one stored message of the `notes` provider: the folder is the account, each subfolder a
|
|
16
|
+
* chat, the path inside the folder the message id. A manifest in the store's sync state says what each
|
|
17
|
+
* file was when last stored, so an untouched file is not even read and an identical one is not saved; an
|
|
18
|
+
* edit keeps the old text as a revision, and a note gone from the folder (or newly ignored) loses its text.
|
|
19
|
+
*/
|
|
20
|
+
export declare const importNotes: (store: MessageStore, folder: string, { ignore, now }?: {
|
|
21
|
+
ignore?: string[];
|
|
22
|
+
now?: () => number;
|
|
23
|
+
}) => Promise<NotesImport>;
|
|
24
|
+
//# sourceMappingURL=import.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../src/notes/import.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAI5E,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAA;IACf,+FAA+F;IAC/F,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAQD,6FAA6F;AAC7F,eAAO,MAAM,QAAQ,WAAY,MAAM,KAAG,UAA+D,CAAA;AAuBzG;;;;;GAKG;AACH,eAAO,MAAM,WAAW,UACf,YAAY,UACX,MAAM,oBACmB;IAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAAE,KACzE,OAAO,CAAC,WAAW,CAyErB,CAAA"}
|