@wirecat/cli-memo 0.0.0 → 0.5.1
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 +204 -0
- package/README.md +315 -0
- package/dist/answers/command.d.ts +35 -0
- package/dist/answers/command.d.ts.map +1 -0
- package/dist/answers/command.js +117 -0
- package/dist/answers/command.js.map +1 -0
- 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 +39 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +40 -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 +73 -0
- package/dist/context/command.js.map +1 -0
- package/dist/context/context.d.ts +28 -0
- package/dist/context/context.d.ts.map +1 -0
- package/dist/context/context.js +49 -0
- package/dist/context/context.js.map +1 -0
- package/dist/knowledge/command.d.ts +4 -0
- package/dist/knowledge/command.d.ts.map +1 -0
- package/dist/knowledge/command.js +158 -0
- package/dist/knowledge/command.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 +61 -0
- package/dist/mail/command.js.map +1 -0
- package/dist/mail/gmail.d.ts +18 -0
- package/dist/mail/gmail.d.ts.map +1 -0
- package/dist/mail/gmail.js +103 -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 +53 -0
- package/dist/mail/import.d.ts.map +1 -0
- package/dist/mail/import.js +170 -0
- package/dist/mail/import.js.map +1 -0
- package/dist/mail/parse.d.ts +21 -0
- package/dist/mail/parse.d.ts.map +1 -0
- package/dist/mail/parse.js +86 -0
- package/dist/mail/parse.js.map +1 -0
- package/dist/notes/about.d.ts +31 -0
- package/dist/notes/about.d.ts.map +1 -0
- package/dist/notes/about.js +69 -0
- package/dist/notes/about.js.map +1 -0
- package/dist/notes/bound.d.ts +24 -0
- package/dist/notes/bound.d.ts.map +1 -0
- package/dist/notes/bound.js +57 -0
- package/dist/notes/bound.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 +222 -0
- package/dist/notes/command.js.map +1 -0
- package/dist/notes/dialects/common.d.ts +18 -0
- package/dist/notes/dialects/common.d.ts.map +1 -0
- package/dist/notes/dialects/common.js +79 -0
- package/dist/notes/dialects/common.js.map +1 -0
- package/dist/notes/dialects/index.d.ts +5 -0
- package/dist/notes/dialects/index.d.ts.map +1 -0
- package/dist/notes/dialects/index.js +6 -0
- package/dist/notes/dialects/index.js.map +1 -0
- package/dist/notes/dialects/markdown.d.ts +7 -0
- package/dist/notes/dialects/markdown.d.ts.map +1 -0
- package/dist/notes/dialects/markdown.js +51 -0
- package/dist/notes/dialects/markdown.js.map +1 -0
- package/dist/notes/dialects/obsidian.d.ts +6 -0
- package/dist/notes/dialects/obsidian.d.ts.map +1 -0
- package/dist/notes/dialects/obsidian.js +33 -0
- package/dist/notes/dialects/obsidian.js.map +1 -0
- package/dist/notes/dialects/types.d.ts +39 -0
- package/dist/notes/dialects/types.d.ts.map +1 -0
- package/dist/notes/dialects/types.js +2 -0
- package/dist/notes/dialects/types.js.map +1 -0
- package/dist/notes/embed.d.ts +51 -0
- package/dist/notes/embed.d.ts.map +1 -0
- package/dist/notes/embed.js +109 -0
- package/dist/notes/embed.js.map +1 -0
- package/dist/notes/export.d.ts +27 -0
- package/dist/notes/export.d.ts.map +1 -0
- package/dist/notes/export.js +76 -0
- package/dist/notes/export.js.map +1 -0
- package/dist/notes/files.d.ts +14 -0
- package/dist/notes/files.d.ts.map +1 -0
- package/dist/notes/files.js +77 -0
- package/dist/notes/files.js.map +1 -0
- package/dist/notes/folders-command.d.ts +4 -0
- package/dist/notes/folders-command.d.ts.map +1 -0
- package/dist/notes/folders-command.js +113 -0
- package/dist/notes/folders-command.js.map +1 -0
- package/dist/notes/folders.d.ts +27 -0
- package/dist/notes/folders.d.ts.map +1 -0
- package/dist/notes/folders.js +46 -0
- package/dist/notes/folders.js.map +1 -0
- package/dist/notes/import.d.ts +33 -0
- package/dist/notes/import.d.ts.map +1 -0
- package/dist/notes/import.js +183 -0
- package/dist/notes/import.js.map +1 -0
- package/dist/notes/internal.d.ts +17 -0
- package/dist/notes/internal.d.ts.map +1 -0
- package/dist/notes/internal.js +47 -0
- package/dist/notes/internal.js.map +1 -0
- package/dist/notes/links.d.ts +18 -0
- package/dist/notes/links.d.ts.map +1 -0
- package/dist/notes/links.js +44 -0
- package/dist/notes/links.js.map +1 -0
- package/dist/notes/renames.d.ts +11 -0
- package/dist/notes/renames.d.ts.map +1 -0
- package/dist/notes/renames.js +24 -0
- package/dist/notes/renames.js.map +1 -0
- package/dist/notes/search.d.ts +5 -0
- package/dist/notes/search.d.ts.map +1 -0
- package/dist/notes/search.js +20 -0
- package/dist/notes/search.js.map +1 -0
- package/dist/notes/state.d.ts +14 -0
- package/dist/notes/state.d.ts.map +1 -0
- package/dist/notes/state.js +21 -0
- package/dist/notes/state.js.map +1 -0
- package/dist/options.d.ts +4 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +12 -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 +57 -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 +13 -0
- package/dist/people/notes-map.d.ts.map +1 -0
- package/dist/people/notes-map.js +49 -0
- package/dist/people/notes-map.js.map +1 -0
- package/dist/people/subject.d.ts +8 -0
- package/dist/people/subject.d.ts.map +1 -0
- package/dist/people/subject.js +19 -0
- package/dist/people/subject.js.map +1 -0
- package/dist/program.d.ts +7 -0
- package/dist/program.d.ts.map +1 -0
- package/dist/program.js +34 -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 +114 -0
- package/dist/run/import.js.map +1 -0
- package/dist/search/command.d.ts +38 -0
- package/dist/search/command.d.ts.map +1 -0
- package/dist/search/command.js +269 -0
- package/dist/search/command.js.map +1 -0
- package/dist/store/gone.d.ts +17 -0
- package/dist/store/gone.d.ts.map +1 -0
- package/dist/store/gone.js +52 -0
- package/dist/store/gone.js.map +1 -0
- package/dist/store/scope.d.ts +19 -0
- package/dist/store/scope.d.ts.map +1 -0
- package/dist/store/scope.js +34 -0
- package/dist/store/scope.js.map +1 -0
- package/dist/store/source-target.d.ts +18 -0
- package/dist/store/source-target.d.ts.map +1 -0
- package/dist/store/source-target.js +28 -0
- package/dist/store/source-target.js.map +1 -0
- package/dist/tags/command.d.ts +4 -0
- package/dist/tags/command.d.ts.map +1 -0
- package/dist/tags/command.js +166 -0
- package/dist/tags/command.js.map +1 -0
- package/dist/tasks/command.d.ts +4 -0
- package/dist/tasks/command.d.ts.map +1 -0
- package/dist/tasks/command.js +83 -0
- package/dist/tasks/command.js.map +1 -0
- package/dist/tasks/context.d.ts +16 -0
- package/dist/tasks/context.d.ts.map +1 -0
- package/dist/tasks/context.js +68 -0
- package/dist/tasks/context.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 +48 -3
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-core";
|
|
2
|
+
import { conversationsService, embeddingsService, embedNotes as embedNoteChunksOf, storeOnlyDeps, } from "@wirecat/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
|
+
const pendingChats = [];
|
|
27
|
+
for (const [index, chat] of (embed ? chats : []).entries()) {
|
|
28
|
+
if (chunks >= maxChunks) {
|
|
29
|
+
left = true;
|
|
30
|
+
pendingChats.push(...chats.slice(index));
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const done = await embeddings.embed(chat, { maxChunks: maxChunks - chunks });
|
|
35
|
+
chunks += done.embedded;
|
|
36
|
+
if ((await embeddings.status(chat)).left > 0) {
|
|
37
|
+
left = true;
|
|
38
|
+
pendingChats.push(chat);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (!isMissingModel(error))
|
|
43
|
+
throw error;
|
|
44
|
+
return {
|
|
45
|
+
chats: chats.length,
|
|
46
|
+
chunks,
|
|
47
|
+
left: true,
|
|
48
|
+
notEmbedded: "the e5-small model is not downloaded — tg models text download e5-small",
|
|
49
|
+
pendingChats: [...pendingChats, ...chats.slice(index)],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
chats: chats.length,
|
|
55
|
+
chunks,
|
|
56
|
+
left,
|
|
57
|
+
...(pendingChats.length ? { pendingChats } : {}),
|
|
58
|
+
...(embed ? {} : { notEmbedded: "embedding is off", pendingChats: chats }),
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
const PENDING = "embed_pending";
|
|
62
|
+
/**
|
|
63
|
+
* Embeds what changed since the last run, and whatever an earlier run left. The folders waiting are kept in
|
|
64
|
+
* the store's sync state, so a run stopped by its bound, or a missing model, picks up next time; the first run
|
|
65
|
+
* after embedding exists takes every folder already stored.
|
|
66
|
+
*/
|
|
67
|
+
export const embedChanged = async (store, key, changed, options = {}) => {
|
|
68
|
+
const saved = await store.syncState(key, PENDING);
|
|
69
|
+
const waiting = saved === undefined
|
|
70
|
+
? (await store.chats(key, { limit: 10_000 })).items.map(({ id }) => id)
|
|
71
|
+
: JSON.parse(saved.value);
|
|
72
|
+
// A build under older rules, or one a change made stale, is rebuilt even when no note changed since.
|
|
73
|
+
const deps = storeOnlyDeps(store, key, { app: APP, ...(options.env === undefined ? {} : { env: options.env }) });
|
|
74
|
+
const behind = (await embeddingsService(deps).readiness({})).chats
|
|
75
|
+
.filter(({ state, graph, vectors }) => state === "stale" || graph?.outdatedRules === true || vectors.missing > 0)
|
|
76
|
+
.map(({ chat }) => chat);
|
|
77
|
+
const chats = [...new Set([...changed, ...waiting, ...behind])];
|
|
78
|
+
if (chats.length === 0)
|
|
79
|
+
return { chats: 0, chunks: 0, left: false };
|
|
80
|
+
const selected = chats.slice(0, options.maxChats ?? 100);
|
|
81
|
+
const result = await embedNotes(store, key, selected, options);
|
|
82
|
+
const pending = [...chats.slice(selected.length), ...(result.pendingChats ?? (result.left ? selected : []))];
|
|
83
|
+
result.left ||= pending.length > 0;
|
|
84
|
+
// Embedding off leaves every chat waiting, so turning it on later embeds them.
|
|
85
|
+
await store.setSyncState(key, PENDING, JSON.stringify(pending));
|
|
86
|
+
return result;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Embeds the notes' chunks that have no vector yet, for search by meaning. Bounded like mail, so a
|
|
90
|
+
* timer run stays short and the next continues. A missing model is reported, never downloaded.
|
|
91
|
+
*/
|
|
92
|
+
export const embedNoteChunks = async (store, { maxChunks = EMBED_PER_RUN, embed = true, env, } = {}) => {
|
|
93
|
+
if (!embed)
|
|
94
|
+
return { chunks: 0, left: true, notEmbedded: "embedding is off" };
|
|
95
|
+
try {
|
|
96
|
+
const done = await embedNoteChunksOf(store, { maxChunks, command: "tg", ...(env === undefined ? {} : { env }) });
|
|
97
|
+
return { chunks: done.embedded, left: done.left };
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (!isMissingModel(error))
|
|
101
|
+
throw error;
|
|
102
|
+
return {
|
|
103
|
+
chunks: 0,
|
|
104
|
+
left: true,
|
|
105
|
+
notEmbedded: "the e5-small model is not downloaded — tg models text download e5-small",
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=embed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embed.js","sourceRoot":"","sources":["../../src/notes/embed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,UAAU,IAAI,iBAAiB,EAC/B,aAAa,GACd,MAAM,iCAAiC,CAAA;AAExC,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAE/B,8JAA8J;AAC9J,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAA;AAYhC,MAAM,cAAc,GAAG,CAAC,KAAc,EAAW,EAAE,CACjD,KAAK,YAAY,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;AAEpG;;;;;GAKG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAC7B,KAAmB,EACnB,GAAe,EACf,KAAe,EACf,EACE,SAAS,GAAG,aAAa,EACzB,KAAK,GAAG,IAAI,EACZ,GAAG,GACJ,GAAqE,EAAE,EAChD,EAAE;IAC1B,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;IAC3F,MAAM,aAAa,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAA;IAChD,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAA;IAC1C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YACvD,mEAAmE;YACnE,IAAI,CAAC,CAAC,KAAK,YAAY,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC;gBAAE,MAAM,KAAK,CAAA;QAC7E,CAAC,CAAC,CAAA;IACJ,CAAC;IACD,IAAI,MAAM,GAAG,CAAC,CAAA;IACd,IAAI,IAAI,GAAG,KAAK,CAAA;IAChB,MAAM,YAAY,GAAa,EAAE,CAAA;IACjC,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3D,IAAI,MAAM,IAAI,SAAS,EAAE,CAAC;YACxB,IAAI,GAAG,IAAI,CAAA;YACX,YAAY,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;YACxC,MAAK;QACP,CAAC;QACD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM,EAAE,CAAC,CAAA;YAC5E,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAA;YACvB,IAAI,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBAC7C,IAAI,GAAG,IAAI,CAAA;gBACX,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACzB,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAA;YACvC,OAAO;gBACL,KAAK,EAAE,KAAK,CAAC,MAAM;gBACnB,MAAM;gBACN,IAAI,EAAE,IAAI;gBACV,WAAW,EAAE,yEAAyE;gBACtF,YAAY,EAAE,CAAC,GAAG,YAAY,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;aACvD,CAAA;QACH,CAAC;IACH,CAAC;IACD,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,MAAM;QACnB,MAAM;QACN,IAAI;QACJ,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChD,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,kBAAkB,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;KAC3E,CAAA;AACH,CAAC,CAAA;AAED,MAAM,OAAO,GAAG,eAAe,CAAA;AAE/B;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAC/B,KAAmB,EACnB,GAAe,EACf,OAAiB,EACjB,OAAO,GAAwF,EAAE,EACzE,EAAE;IAC1B,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IACjD,MAAM,OAAO,GACX,KAAK,KAAK,SAAS;QACjB,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;QACvE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC7B,qGAAqG;IACrG,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;IAChH,MAAM,MAAM,GAAG,CAAC,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK;SAC/D,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,KAAK,KAAK,OAAO,IAAI,KAAK,EAAE,aAAa,KAAK,IAAI,IAAI,OAAO,CAAC,OAAO,GAAG,CAAC,CAAC;SAChH,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAA;IAC1B,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAS,CAAC,GAAG,OAAO,EAAE,GAAG,OAAO,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;IACvE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;IACnE,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,QAAQ,IAAI,GAAG,CAAC,CAAA;IACxD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;IAC9D,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IAC5G,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC,MAAM,GAAG,CAAC,CAAA;IAClC,+EAA+E;IAC/E,MAAM,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAA;IAC/D,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAUD;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAClC,KAAmB,EACnB,EACE,SAAS,GAAG,aAAa,EACzB,KAAK,GAAG,IAAI,EACZ,GAAG,GACJ,GAAqE,EAAE,EAC3C,EAAE;IAC/B,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAA;IAC7E,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;QAChH,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAA;IACnD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAA;QACvC,OAAO;YACL,MAAM,EAAE,CAAC;YACT,IAAI,EAAE,IAAI;YACV,WAAW,EAAE,yEAAyE;SACvF,CAAA;IACH,CAAC;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { NoteDialect, NoteForExport } from "./dialects/index.js";
|
|
2
|
+
export interface ExportedNote extends NoteForExport {
|
|
3
|
+
/** The note's id in the store; written as `memo-id` so import skips the file. */
|
|
4
|
+
id: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ExportResult {
|
|
7
|
+
dir: string;
|
|
8
|
+
written: string[];
|
|
9
|
+
unchanged: string[];
|
|
10
|
+
/** Files memo wrote that were edited since: left as they are, so the edit is not lost. */
|
|
11
|
+
edited: {
|
|
12
|
+
path: string;
|
|
13
|
+
id: string;
|
|
14
|
+
}[];
|
|
15
|
+
}
|
|
16
|
+
export declare const editedSinceExport: (content: string) => boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Writes each note to `dir` in `dialect`'s format. A file keeps its name across exports (found by its
|
|
19
|
+
* `memo-id`), is rewritten only while it is still what memo wrote, and a file memo did not write is never
|
|
20
|
+
* touched. Nothing is deleted.
|
|
21
|
+
*/
|
|
22
|
+
export declare const exportNotes: ({ dir, dialect, notes, }: {
|
|
23
|
+
dir: string;
|
|
24
|
+
dialect: NoteDialect;
|
|
25
|
+
notes: ExportedNote[];
|
|
26
|
+
}) => ExportResult;
|
|
27
|
+
//# sourceMappingURL=export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/notes/export.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAGrE,MAAM,WAAW,YAAa,SAAQ,aAAa;IACjD,iFAAiF;IACjF,EAAE,EAAE,MAAM,CAAA;CACX;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,0FAA0F;IAC1F,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CACvC;AAkBD,eAAO,MAAM,iBAAiB,YAAa,MAAM,KAAG,OAGnD,CAAA;AAkBD;;;;GAIG;AACH,eAAO,MAAM,WAAW,6BAIrB;IACD,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,WAAW,CAAA;IACpB,KAAK,EAAE,YAAY,EAAE,CAAA;CACtB,KAAG,YAkCH,CAAA"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { readMemoId } from "./files.js";
|
|
5
|
+
const HASH_LINE = /^memo-hash: .*\r?\n/m;
|
|
6
|
+
const PLACEHOLDER = "f".repeat(64);
|
|
7
|
+
const UNSAFE = /[\\/:*?"<>|#^[\]\p{Cc}]/gu;
|
|
8
|
+
const MAX_NAME = 120;
|
|
9
|
+
const sha256 = (text) => createHash("sha256").update(text).digest("hex");
|
|
10
|
+
/**
|
|
11
|
+
* The hash covers the file minus its own `memo-hash` line, so a file can prove it is still what memo
|
|
12
|
+
* wrote without memo keeping any record of its own.
|
|
13
|
+
*/
|
|
14
|
+
const withHash = (content) => {
|
|
15
|
+
const hash = sha256(content.replace(HASH_LINE, ""));
|
|
16
|
+
return content.replace(`memo-hash: ${PLACEHOLDER}`, `memo-hash: ${hash}`);
|
|
17
|
+
};
|
|
18
|
+
export const editedSinceExport = (content) => {
|
|
19
|
+
const stored = /^memo-hash: ([0-9a-f]{64})$/m.exec(content)?.[1];
|
|
20
|
+
return stored === undefined || sha256(content.replace(HASH_LINE, "")) !== stored;
|
|
21
|
+
};
|
|
22
|
+
const fileName = (title, id, taken) => {
|
|
23
|
+
const base = title.replace(UNSAFE, " ").replace(/\s+/g, " ").trim().slice(0, MAX_NAME) || id;
|
|
24
|
+
const plain = `${base}.md`;
|
|
25
|
+
return taken(plain) ? `${base} (${id.slice(-8)}).md` : plain;
|
|
26
|
+
};
|
|
27
|
+
const exportedIn = (dir) => {
|
|
28
|
+
const found = new Map();
|
|
29
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
30
|
+
if (!entry.isFile() || !/\.(md|markdown)$/i.test(entry.name))
|
|
31
|
+
continue;
|
|
32
|
+
const id = readMemoId(join(dir, entry.name));
|
|
33
|
+
if (id !== null)
|
|
34
|
+
found.set(id, entry.name);
|
|
35
|
+
}
|
|
36
|
+
return found;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Writes each note to `dir` in `dialect`'s format. A file keeps its name across exports (found by its
|
|
40
|
+
* `memo-id`), is rewritten only while it is still what memo wrote, and a file memo did not write is never
|
|
41
|
+
* touched. Nothing is deleted.
|
|
42
|
+
*/
|
|
43
|
+
export const exportNotes = ({ dir, dialect, notes, }) => {
|
|
44
|
+
mkdirSync(dir, { recursive: true });
|
|
45
|
+
const existing = exportedIn(dir);
|
|
46
|
+
const claimed = new Set(existing.values());
|
|
47
|
+
const result = { dir, written: [], unchanged: [], edited: [] };
|
|
48
|
+
for (const { id, ...note } of notes) {
|
|
49
|
+
const frontMatter = { "memo-id": id, "memo-hash": PLACEHOLDER, ...note.frontMatter };
|
|
50
|
+
frontMatter["memo-id"] = id;
|
|
51
|
+
frontMatter["memo-hash"] = PLACEHOLDER;
|
|
52
|
+
const content = withHash(dialect.render({ ...note, frontMatter }));
|
|
53
|
+
const known = existing.get(id);
|
|
54
|
+
const name = known ?? fileName(note.title, id, (candidate) => claimed.has(candidate) || existsSync(join(dir, candidate)));
|
|
55
|
+
claimed.add(name);
|
|
56
|
+
const path = join(dir, name);
|
|
57
|
+
if (known !== undefined) {
|
|
58
|
+
const current = readFileSync(path, "utf8");
|
|
59
|
+
if (editedSinceExport(current)) {
|
|
60
|
+
result.edited.push({ path, id });
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (current === content) {
|
|
64
|
+
result.unchanged.push(path);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Hidden, so an import running meanwhile never lists a half-written file.
|
|
69
|
+
const partial = join(dir, `.${name}.memo-tmp`);
|
|
70
|
+
writeFileSync(partial, content);
|
|
71
|
+
renameSync(partial, path);
|
|
72
|
+
result.written.push(path);
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/notes/export.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const NOTE_EXTENSIONS: string[];
|
|
2
|
+
/** The `memo-id` of a file memo exported, read from the front matter at its start; `null` for any other file. */
|
|
3
|
+
export declare const readMemoId: (path: string) => string | null;
|
|
4
|
+
export declare const isExported: (path: string) => boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Note files under `folder`, as absolute paths. Hidden entries (`.obsidian`, `.trash`) are skipped, and
|
|
7
|
+
* so is whatever an `ignore` rule names — a path inside the folder (`Psychology/Personal Notes.md`,
|
|
8
|
+
* `Archive`) or a glob (`**\/Private*`, `*.txt`). A note memo exported is skipped too: it is already in
|
|
9
|
+
* the store, and importing it would make it a second note.
|
|
10
|
+
*/
|
|
11
|
+
export declare const noteFiles: (folder: string, ignore?: string[], extensions?: string[], { skipExported }?: {
|
|
12
|
+
skipExported?: boolean;
|
|
13
|
+
}) => string[];
|
|
14
|
+
//# 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,UAe3B,CAAA;AAKD,iHAAiH;AACjH,eAAO,MAAM,UAAU,SAAU,MAAM,KAAG,MAAM,GAAG,IAUlD,CAAA;AAID,eAAO,MAAM,UAAU,SAAU,MAAM,KAAG,OAA4D,CAAA;AAoBtG;;;;;GAKG;AACH,eAAO,MAAM,SAAS,WACZ,MAAM,WACN,MAAM,EAAE,4CAES;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,KAClD,MAAM,EAgBR,CAAA"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { closeSync, openSync, readdirSync, readSync } from "node:fs";
|
|
2
|
+
import { join, relative, sep } from "node:path";
|
|
3
|
+
export const NOTE_EXTENSIONS = [
|
|
4
|
+
".md",
|
|
5
|
+
".markdown",
|
|
6
|
+
".txt",
|
|
7
|
+
".csv",
|
|
8
|
+
".tsv",
|
|
9
|
+
".pdf",
|
|
10
|
+
".docx",
|
|
11
|
+
".xlsx",
|
|
12
|
+
".odt",
|
|
13
|
+
".ods",
|
|
14
|
+
".pptx",
|
|
15
|
+
".epub",
|
|
16
|
+
".doc",
|
|
17
|
+
".xls",
|
|
18
|
+
];
|
|
19
|
+
const MEMO_ID = /^---\r?\n(?:[^\n]*\n)*?memo-id:[ \t]*["']?([^"'\s]+)/;
|
|
20
|
+
const HEAD = 1024;
|
|
21
|
+
/** The `memo-id` of a file memo exported, read from the front matter at its start; `null` for any other file. */
|
|
22
|
+
export const readMemoId = (path) => {
|
|
23
|
+
const buffer = Buffer.alloc(HEAD);
|
|
24
|
+
const descriptor = openSync(path, "r");
|
|
25
|
+
try {
|
|
26
|
+
const head = buffer.subarray(0, readSync(descriptor, buffer, 0, HEAD, 0)).toString("utf8");
|
|
27
|
+
const end = head.indexOf("\n---", 3);
|
|
28
|
+
return MEMO_ID.exec(end < 0 ? head : head.slice(0, end + 1))?.[1] ?? null;
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
closeSync(descriptor);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const TEXT_NOTE = /\.(md|markdown)$/i;
|
|
35
|
+
export const isExported = (path) => TEXT_NOTE.test(path) && readMemoId(path) !== null;
|
|
36
|
+
const pattern = (rule) => {
|
|
37
|
+
const glob = rule.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
|
|
38
|
+
const body = glob
|
|
39
|
+
.split(/(\*\*\/?|\*|\?)/)
|
|
40
|
+
.map((part) => part === "**/" || part === "**"
|
|
41
|
+
? ".*"
|
|
42
|
+
: part === "*"
|
|
43
|
+
? "[^/]*"
|
|
44
|
+
: part === "?"
|
|
45
|
+
? "[^/]"
|
|
46
|
+
: part.replace(/[.+^${}()|[\]\\]/g, "\\$&"))
|
|
47
|
+
.join("");
|
|
48
|
+
// A rule names a path inside the folder, and everything under it when it is a folder.
|
|
49
|
+
return new RegExp(`^${body}(/.*)?$`, "i");
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Note files under `folder`, as absolute paths. Hidden entries (`.obsidian`, `.trash`) are skipped, and
|
|
53
|
+
* so is whatever an `ignore` rule names — a path inside the folder (`Psychology/Personal Notes.md`,
|
|
54
|
+
* `Archive`) or a glob (`**\/Private*`, `*.txt`). A note memo exported is skipped too: it is already in
|
|
55
|
+
* the store, and importing it would make it a second note.
|
|
56
|
+
*/
|
|
57
|
+
export const noteFiles = (folder, ignore = [], extensions = NOTE_EXTENSIONS, { skipExported = true } = {}) => {
|
|
58
|
+
const rules = ignore.map(pattern);
|
|
59
|
+
const ignored = (path) => {
|
|
60
|
+
const inside = relative(folder, path).split(sep).join("/");
|
|
61
|
+
return rules.some((rule) => rule.test(inside));
|
|
62
|
+
};
|
|
63
|
+
const walk = (dir) => readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
64
|
+
if (entry.name.startsWith("."))
|
|
65
|
+
return [];
|
|
66
|
+
const path = join(dir, entry.name);
|
|
67
|
+
if (ignored(path))
|
|
68
|
+
return [];
|
|
69
|
+
if (entry.isDirectory())
|
|
70
|
+
return walk(path);
|
|
71
|
+
if (!entry.isFile() || !extensions.some((ext) => entry.name.toLowerCase().endsWith(ext)))
|
|
72
|
+
return [];
|
|
73
|
+
return skipExported && isExported(path) ? [] : [path];
|
|
74
|
+
});
|
|
75
|
+
return walk(folder);
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/notes/files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folders-command.d.ts","sourceRoot":"","sources":["../../src/notes/folders-command.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4B,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAE1E,OAAO,EAAE,KAAK,OAAO,EAAwB,MAAM,WAAW,CAAA;AAwB9D,eAAO,MAAM,cAAc,YAAa,OAAO,WAAW,OAAO,OAAO,MAAM,CAAC,UAAU,KAAG,IA0F3F,CAAA"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { statSync } from "node:fs";
|
|
2
|
+
import { basename, resolve } from "node:path";
|
|
3
|
+
import { CliError, createRenderer } from "@wirecat/cli-core";
|
|
4
|
+
import { openStore } from "@wirecat/cli-messaging/store";
|
|
5
|
+
import { InvalidArgumentError } from "commander";
|
|
6
|
+
import { loadConfig, updateConfig } from "../config.js";
|
|
7
|
+
import { claimPendingFolders } from "./bound.js";
|
|
8
|
+
import { DIALECTS } from "./dialects/index.js";
|
|
9
|
+
import { attachFolder, bindFolder, noteFolders } from "./folders.js";
|
|
10
|
+
const formatOf = (value) => {
|
|
11
|
+
if (!DIALECTS.includes(value))
|
|
12
|
+
throw new InvalidArgumentError(`choose ${DIALECTS.join(", ")}`);
|
|
13
|
+
return value;
|
|
14
|
+
};
|
|
15
|
+
const directory = (path) => {
|
|
16
|
+
const at = resolve(path);
|
|
17
|
+
let isDirectory = false;
|
|
18
|
+
try {
|
|
19
|
+
isDirectory = statSync(at).isDirectory();
|
|
20
|
+
}
|
|
21
|
+
catch { }
|
|
22
|
+
if (!isDirectory)
|
|
23
|
+
throw new CliError("not_found", `${at} is not a folder`);
|
|
24
|
+
return at;
|
|
25
|
+
};
|
|
26
|
+
const line = (folder) => `${folder.id ?? "(no id — memo folders add <path>)"} ${folder.format} ${folder.path}`;
|
|
27
|
+
export const foldersCommand = (program, streams, env) => {
|
|
28
|
+
const print = (json, value, text) => {
|
|
29
|
+
if (json)
|
|
30
|
+
createRenderer({ format: "json", color: false, streams }).result(value);
|
|
31
|
+
else
|
|
32
|
+
streams.data(`${text}\n`);
|
|
33
|
+
};
|
|
34
|
+
const withStore = async (work) => {
|
|
35
|
+
const store = await openStore({ env });
|
|
36
|
+
try {
|
|
37
|
+
await claimPendingFolders(store, env);
|
|
38
|
+
return await work(store);
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
await store.close();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const folders = program
|
|
45
|
+
.command("folders")
|
|
46
|
+
.description("Folders of notes: an id that links and the store use, and where the folder is on this computer");
|
|
47
|
+
folders
|
|
48
|
+
.command("add")
|
|
49
|
+
.description("Give a folder of notes an id, or show the one it has")
|
|
50
|
+
.argument("<path>", "the folder on this computer")
|
|
51
|
+
.option("--format <format>", `how its notes are written: ${DIALECTS.join(", ")} (default: obsidian)`, formatOf)
|
|
52
|
+
.option("--json", "print JSON")
|
|
53
|
+
.action(async (path, options) => {
|
|
54
|
+
const at = directory(path);
|
|
55
|
+
const answer = await withStore(async (store) => {
|
|
56
|
+
const known = new Set((await store.notes.folders()).map(({ id }) => id));
|
|
57
|
+
const current = noteFolders(loadConfig(env).notes).find((folder) => folder.path === at);
|
|
58
|
+
if (current?.id && known.has(current.id)) {
|
|
59
|
+
const folder = { ...current, id: current.id, format: options.format ?? current.format };
|
|
60
|
+
if (folder.format !== current.format)
|
|
61
|
+
updateConfig((config) => bindFolder(config, folder), env);
|
|
62
|
+
return { ...folder, created: false };
|
|
63
|
+
}
|
|
64
|
+
const format = options.format ?? current?.format ?? "obsidian";
|
|
65
|
+
const made = await store.notes.addFolder({ name: basename(at) || at, format });
|
|
66
|
+
const folder = { id: made.id, path: at, format };
|
|
67
|
+
updateConfig((config) => bindFolder(config, folder), env);
|
|
68
|
+
return { ...folder, created: true };
|
|
69
|
+
});
|
|
70
|
+
print(options.json, answer, answer.id);
|
|
71
|
+
});
|
|
72
|
+
folders
|
|
73
|
+
.command("attach")
|
|
74
|
+
.description("Bind a folder id from another computer to the folder's path on this one")
|
|
75
|
+
.argument("<id>", "the folder's id, as memo folders add printed it")
|
|
76
|
+
.argument("<path>", "the same folder on this computer")
|
|
77
|
+
.option("--format <format>", `how its notes are written: ${DIALECTS.join(", ")}`, formatOf)
|
|
78
|
+
.option("--json", "print JSON")
|
|
79
|
+
.action(async (id, path, options) => {
|
|
80
|
+
const at = directory(path);
|
|
81
|
+
const folder = await withStore(async (store) => {
|
|
82
|
+
const stored = (await store.notes.folders()).find((candidate) => candidate.id === id);
|
|
83
|
+
if (!stored)
|
|
84
|
+
throw new CliError("not_found", `the store has no folder ${id} — memo folders list shows those it has`);
|
|
85
|
+
let bound;
|
|
86
|
+
updateConfig((config) => {
|
|
87
|
+
const result = attachFolder(config, id, at, options.format ?? stored.format);
|
|
88
|
+
bound = result.folder;
|
|
89
|
+
return result.config;
|
|
90
|
+
}, env);
|
|
91
|
+
return bound;
|
|
92
|
+
});
|
|
93
|
+
print(options.json, folder, line(folder));
|
|
94
|
+
});
|
|
95
|
+
folders
|
|
96
|
+
.command("list")
|
|
97
|
+
.description("The folders of notes: those on this computer, and those the store has from another")
|
|
98
|
+
.option("--json", "print JSON")
|
|
99
|
+
.action(async (options) => {
|
|
100
|
+
const answer = await withStore(async (store) => {
|
|
101
|
+
const here = noteFolders(loadConfig(env).notes);
|
|
102
|
+
const elsewhere = (await store.notes.folders())
|
|
103
|
+
.filter((folder) => !here.some((entry) => entry.id === folder.id))
|
|
104
|
+
.map(({ id, name, format }) => ({ id, name, format }));
|
|
105
|
+
return { items: here, elsewhere };
|
|
106
|
+
});
|
|
107
|
+
print(options.json, answer, [
|
|
108
|
+
...answer.items.map(line),
|
|
109
|
+
...answer.elsewhere.map(({ id, name }) => `${id} not on this computer (${name}) — memo folders attach ${id} <path>`),
|
|
110
|
+
].join("\n") || "no folders: memo folders add <path>");
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
//# sourceMappingURL=folders-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folders-command.js","sourceRoot":"","sources":["../../src/notes/folders-command.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Config } from "../config.js";
|
|
2
|
+
import type { DialectName } from "./dialects/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* A folder of notes on this computer. The id is what the store and every link use; the path is only
|
|
5
|
+
* where the folder is here, so the same folder can live elsewhere on another computer.
|
|
6
|
+
*/
|
|
7
|
+
export interface NoteFolder {
|
|
8
|
+
/** `null` for a folder still written as a bare path, until `memo folders add` gives it an id. */
|
|
9
|
+
id: string | null;
|
|
10
|
+
path: string;
|
|
11
|
+
format: DialectName;
|
|
12
|
+
}
|
|
13
|
+
export declare const noteFolders: (notes: Config["notes"]) => NoteFolder[];
|
|
14
|
+
export declare const folderPaths: (notes: Config["notes"]) => string[];
|
|
15
|
+
/** Writes `folder` at its path, in place of whatever entry had that path or that id. */
|
|
16
|
+
export declare const bindFolder: (config: Config, folder: NoteFolder & {
|
|
17
|
+
id: string;
|
|
18
|
+
}) => Config;
|
|
19
|
+
/**
|
|
20
|
+
* Binds a folder id made elsewhere to its path on this computer. An id already here moves to the new
|
|
21
|
+
* path; a path already bound to another id is refused, since its notes would change identity.
|
|
22
|
+
*/
|
|
23
|
+
export declare const attachFolder: (config: Config, id: string, path: string, format?: DialectName) => {
|
|
24
|
+
config: Config;
|
|
25
|
+
folder: NoteFolder;
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=folders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folders.d.ts","sourceRoot":"","sources":["../../src/notes/folders.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAEtD;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,iGAAiG;IACjG,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,WAAW,CAAA;CACpB;AASD,eAAO,MAAM,WAAW,UAAW,MAAM,CAAC,OAAO,CAAC,KAAG,UAAU,EAAyC,CAAA;AAExG,eAAO,MAAM,WAAW,UAAW,MAAM,CAAC,OAAO,CAAC,KAAG,MAAM,EAAgD,CAAA;AAgB3G,wFAAwF;AACxF,eAAO,MAAM,UAAU,WAAY,MAAM,UAAU,UAAU,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,KAAG,MAShF,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,WACf,MAAM,MACV,MAAM,QACJ,MAAM,WACH,WAAW,KACnB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAatC,CAAA"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { CliError } from "@wirecat/cli-core";
|
|
3
|
+
const entryOf = (entry) => typeof entry === "string"
|
|
4
|
+
? { id: null, path: resolve(entry), format: "obsidian" }
|
|
5
|
+
: { id: entry.id, path: resolve(entry.path), format: entry.format ?? "obsidian" };
|
|
6
|
+
export const noteFolders = (notes) => (notes?.folders ?? []).map(entryOf);
|
|
7
|
+
export const folderPaths = (notes) => noteFolders(notes).map(({ path }) => path);
|
|
8
|
+
const written = (folder) => ({
|
|
9
|
+
id: folder.id,
|
|
10
|
+
path: folder.path,
|
|
11
|
+
...(folder.format === "obsidian" ? {} : { format: folder.format }),
|
|
12
|
+
});
|
|
13
|
+
const withFolders = (config, folders) => ({
|
|
14
|
+
...config,
|
|
15
|
+
notes: {
|
|
16
|
+
...config.notes,
|
|
17
|
+
folders: folders.map((folder) => (folder.id === null ? folder.path : written(folder))),
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
/** Writes `folder` at its path, in place of whatever entry had that path or that id. */
|
|
21
|
+
export const bindFolder = (config, folder) => {
|
|
22
|
+
const at = resolve(folder.path);
|
|
23
|
+
const folders = noteFolders(config.notes);
|
|
24
|
+
const index = folders.findIndex((other) => other.path === at);
|
|
25
|
+
const bound = { ...folder, path: at };
|
|
26
|
+
const kept = folders
|
|
27
|
+
.map((other, position) => (position === index ? bound : other))
|
|
28
|
+
.filter((other, position) => position === index || (other.id !== folder.id && other.path !== at));
|
|
29
|
+
return withFolders(config, index < 0 ? [...kept, bound] : kept);
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Binds a folder id made elsewhere to its path on this computer. An id already here moves to the new
|
|
33
|
+
* path; a path already bound to another id is refused, since its notes would change identity.
|
|
34
|
+
*/
|
|
35
|
+
export const attachFolder = (config, id, path, format) => {
|
|
36
|
+
const at = resolve(path);
|
|
37
|
+
const folders = noteFolders(config.notes);
|
|
38
|
+
const taken = folders.find((folder) => folder.path === at && folder.id !== null && folder.id !== id);
|
|
39
|
+
if (taken)
|
|
40
|
+
throw new CliError("validation_error", `${at} is already folder ${taken.id} — attach that id elsewhere first, or remove it from the config`);
|
|
41
|
+
const previous = folders.find((folder) => folder.id === id);
|
|
42
|
+
const folder = { id, path: at, format: format ?? previous?.format ?? "obsidian" };
|
|
43
|
+
const kept = folders.filter((other) => other.id !== id && other.path !== at);
|
|
44
|
+
return { config: withFolders(config, [...kept, folder]), folder };
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=folders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folders.js","sourceRoot":"","sources":["../../src/notes/folders.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type LoadEngine } from "@wirecat/cli-messaging/documents";
|
|
2
|
+
import type { MessageStore, Note } from "@wirecat/cli-messaging/store";
|
|
3
|
+
import type { BoundFolder } from "./bound.js";
|
|
4
|
+
export interface NotesImport {
|
|
5
|
+
folder: string;
|
|
6
|
+
folderId: string;
|
|
7
|
+
notes: number;
|
|
8
|
+
/** Saved because they are new or their text changed. */
|
|
9
|
+
changed: number;
|
|
10
|
+
/** Moved inside the folder: same note, same links and tags. */
|
|
11
|
+
renamed: number;
|
|
12
|
+
deleted: number;
|
|
13
|
+
deletionsSkipped?: string;
|
|
14
|
+
notRead?: {
|
|
15
|
+
path: string;
|
|
16
|
+
status: string;
|
|
17
|
+
engine?: string;
|
|
18
|
+
}[];
|
|
19
|
+
truncated?: string[];
|
|
20
|
+
}
|
|
21
|
+
export declare const folderNotes: (store: MessageStore, folderId: string) => Promise<Note[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Each file is one note of its folder, named by its path inside the folder. What this computer last saw of
|
|
24
|
+
* each file is kept beside the config (`state.ts`), so an untouched file is not even opened; the store's own
|
|
25
|
+
* content hash keeps an unchanged file from being saved again on a computer importing it for the first time.
|
|
26
|
+
* A file gone from one path whose content appears at a new one is a move. A file memo exported is not a note.
|
|
27
|
+
*/
|
|
28
|
+
export declare const importNotes: (store: MessageStore, folder: BoundFolder, { ignore, env, loadEngine, }?: {
|
|
29
|
+
ignore?: string[];
|
|
30
|
+
env?: NodeJS.ProcessEnv;
|
|
31
|
+
loadEngine?: LoadEngine;
|
|
32
|
+
}) => Promise<NotesImport>;
|
|
33
|
+
//# 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,EAA6B,KAAK,UAAU,EAAkB,MAAM,kCAAkC,CAAA;AAC7G,OAAO,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAA;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAO7C,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAA;IACf,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC7D,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;CACrB;AAWD,eAAO,MAAM,WAAW,UAAiB,YAAY,YAAY,MAAM,KAAG,OAAO,CAAC,IAAI,EAAE,CAOvF,CAAA;AAgBD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,UACf,YAAY,UACX,WAAW,iCAKhB;IAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAAC,UAAU,CAAC,EAAE,UAAU,CAAA;CAAE,KACzE,OAAO,CAAC,WAAW,CA+IrB,CAAA"}
|