@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":"program.js","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAgB,MAAM,mBAAmB,CAAA;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,EAC5B,OAAO,GAAG,cAAc,EACxB,GAAG,GAAG,OAAO,CAAC,GAAG,GAClB,GAGG,EAAE,EAAW,EAAE;IACjB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;SAChC,WAAW,CAAC,wDAAwD,CAAC;SACrE,OAAO,CAAC,OAAO,CAAC,CAAA;IAEnB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAA;IACrC,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAA;IACnC,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAA;IAClC,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAA;IAClC,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAA;IACpC,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAA;IAElC,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type Streams } from "@leemour/cli-core";
|
|
2
|
+
import { type Command } from "commander";
|
|
3
|
+
import { type Config } from "../config.js";
|
|
4
|
+
export declare const DEFAULT_EVERY = "5m";
|
|
5
|
+
/** `5m`, `90s`, `1h` → systemd's `5min`, `90s`, `1h`; under a minute is refused. */
|
|
6
|
+
export declare const interval: (value: string) => string;
|
|
7
|
+
export declare const serviceUnit: (env: NodeJS.ProcessEnv) => string;
|
|
8
|
+
export declare const timerUnit: (every: string) => string;
|
|
9
|
+
export interface AutoState {
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
every: string;
|
|
12
|
+
}
|
|
13
|
+
export declare const autoState: (config: Config) => AutoState;
|
|
14
|
+
/**
|
|
15
|
+
* Makes the timer match the config: on with its interval, or gone. The config is the one source, so a
|
|
16
|
+
* hand edit of `auto` takes effect at the next timer run, which calls this first.
|
|
17
|
+
*/
|
|
18
|
+
export declare const applyAuto: (env: NodeJS.ProcessEnv, state: AutoState) => Promise<void>;
|
|
19
|
+
export declare const autoCommand: (program: Command, streams: Streams, env: NodeJS.ProcessEnv) => void;
|
|
20
|
+
//# sourceMappingURL=auto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto.d.ts","sourceRoot":"","sources":["../../src/run/auto.ts"],"names":[],"mappings":"AAKA,OAAO,EAA4B,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAC1E,OAAO,EAAE,KAAK,OAAO,EAAwB,MAAM,WAAW,CAAA;AAC9D,OAAO,EAAE,KAAK,MAAM,EAA4B,MAAM,cAAc,CAAA;AAEpE,eAAO,MAAM,aAAa,OAAO,CAAA;AAGjC,oFAAoF;AACpF,eAAO,MAAM,QAAQ,UAAW,MAAM,KAAG,MAKxC,CAAA;AAWD,eAAO,MAAM,WAAW,QAAS,MAAM,CAAC,UAAU,KAAG,MAYvC,CAAA;AAEd,eAAO,MAAM,SAAS,UAAW,MAAM,KAAG,MAa5B,CAAA;AAad,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,eAAO,MAAM,SAAS,WAAY,MAAM,KAAG,SAGzC,CAAA;AAEF;;;GAGG;AACH,eAAO,MAAM,SAAS,QAAe,MAAM,CAAC,UAAU,SAAS,SAAS,KAAG,OAAO,CAAC,IAAI,CAwBtF,CAAA;AAED,eAAO,MAAM,WAAW,YAAa,OAAO,WAAW,OAAO,OAAO,MAAM,CAAC,UAAU,KAAG,IAiExF,CAAA"}
|
package/dist/run/auto.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { CliError, createRenderer } from "@leemour/cli-core";
|
|
7
|
+
import { InvalidArgumentError } from "commander";
|
|
8
|
+
import { loadConfig, updateConfig } from "../config.js";
|
|
9
|
+
export const DEFAULT_EVERY = "5m";
|
|
10
|
+
const UNIT = "memo-import";
|
|
11
|
+
/** `5m`, `90s`, `1h` → systemd's `5min`, `90s`, `1h`; under a minute is refused. */
|
|
12
|
+
export const interval = (value) => {
|
|
13
|
+
const match = /^(\d+)(s|m|h)$/.exec(value.trim());
|
|
14
|
+
const seconds = match === null ? 0 : Number(match[1]) * { s: 1, m: 60, h: 3600 }[match[2]];
|
|
15
|
+
if (seconds < 60)
|
|
16
|
+
throw new InvalidArgumentError("an interval of a minute or more, like 5m, 90s or 1h");
|
|
17
|
+
return value.trim();
|
|
18
|
+
};
|
|
19
|
+
const systemdSpan = (every) => every.replace(/m$/, "min");
|
|
20
|
+
const unitDir = (env) => join(env.XDG_CONFIG_HOME ?? join(env.HOME ?? homedir(), ".config"), "systemd", "user");
|
|
21
|
+
const quote = (arg) => `"${arg.replace(/(["\\])/g, "\\$1")}"`;
|
|
22
|
+
const memoBin = () => resolve(dirname(fileURLToPath(import.meta.url)), "../bin/memo.js");
|
|
23
|
+
export const serviceUnit = (env) => [
|
|
24
|
+
"[Unit]",
|
|
25
|
+
"Description=memo: import notes and mail into the shared store",
|
|
26
|
+
"",
|
|
27
|
+
"[Service]",
|
|
28
|
+
"Type=oneshot",
|
|
29
|
+
`ExecStart=${[process.execPath, memoBin(), "import"].map(quote).join(" ")}`,
|
|
30
|
+
// Himalaya and the keyring helper it calls are found on the PATH memo was set up with.
|
|
31
|
+
`Environment=${quote(`PATH=${env.PATH ?? ""}`)} MEMO_AUTO=1`,
|
|
32
|
+
"Nice=10",
|
|
33
|
+
"",
|
|
34
|
+
].join("\n");
|
|
35
|
+
export const timerUnit = (every) => [
|
|
36
|
+
"[Unit]",
|
|
37
|
+
`Description=memo: import notes and mail every ${every}`,
|
|
38
|
+
"",
|
|
39
|
+
"[Timer]",
|
|
40
|
+
"OnActiveSec=1min",
|
|
41
|
+
`OnUnitActiveSec=${systemdSpan(every)}`,
|
|
42
|
+
"AccuracySec=30s",
|
|
43
|
+
"",
|
|
44
|
+
"[Install]",
|
|
45
|
+
"WantedBy=timers.target",
|
|
46
|
+
"",
|
|
47
|
+
].join("\n");
|
|
48
|
+
const systemctl = (env) => (...args) => new Promise((done, fail) => execFile(env.MEMO_SYSTEMCTL ?? "systemctl", ["--user", ...args], { env }, (error, stdout, stderr) => error === null
|
|
49
|
+
? done(stdout)
|
|
50
|
+
: fail(new CliError("provider_error", `systemctl ${args.join(" ")}: ${stderr.trim() || error.message}`))));
|
|
51
|
+
export const autoState = (config) => ({
|
|
52
|
+
enabled: config.auto?.enabled ?? false,
|
|
53
|
+
every: config.auto?.every ?? DEFAULT_EVERY,
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* Makes the timer match the config: on with its interval, or gone. The config is the one source, so a
|
|
57
|
+
* hand edit of `auto` takes effect at the next timer run, which calls this first.
|
|
58
|
+
*/
|
|
59
|
+
export const applyAuto = async (env, state) => {
|
|
60
|
+
const run = systemctl(env);
|
|
61
|
+
const dir = unitDir(env);
|
|
62
|
+
const timerPath = join(dir, `${UNIT}.timer`);
|
|
63
|
+
const servicePath = join(dir, `${UNIT}.service`);
|
|
64
|
+
if (!state.enabled) {
|
|
65
|
+
if (!existsSync(timerPath) && !existsSync(servicePath))
|
|
66
|
+
return;
|
|
67
|
+
await run("disable", "--now", `${UNIT}.timer`).catch(() => "");
|
|
68
|
+
rmSync(timerPath, { force: true });
|
|
69
|
+
rmSync(servicePath, { force: true });
|
|
70
|
+
await run("daemon-reload");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const wanted = { [servicePath]: serviceUnit(env), [timerPath]: timerUnit(state.every) };
|
|
74
|
+
const stale = Object.entries(wanted).filter(([path, text]) => !existsSync(path) || readFileSync(path, "utf8") !== text);
|
|
75
|
+
if (stale.length === 0)
|
|
76
|
+
return;
|
|
77
|
+
mkdirSync(dir, { recursive: true });
|
|
78
|
+
for (const [path, text] of stale)
|
|
79
|
+
writeFileSync(path, text);
|
|
80
|
+
await run("daemon-reload");
|
|
81
|
+
await run("enable", "--now", `${UNIT}.timer`);
|
|
82
|
+
// A timer already running keeps its old interval until restarted.
|
|
83
|
+
if (stale.some(([path]) => path === timerPath))
|
|
84
|
+
await run("restart", `${UNIT}.timer`);
|
|
85
|
+
};
|
|
86
|
+
export const autoCommand = (program, streams, env) => {
|
|
87
|
+
const run = systemctl(env);
|
|
88
|
+
const auto = program
|
|
89
|
+
.command("auto")
|
|
90
|
+
.description("Run memo import on a timer (systemd user timer); on/off and the interval live in the config");
|
|
91
|
+
auto
|
|
92
|
+
.command("on")
|
|
93
|
+
.description("Import every --every (default: auto.every in the config, else 5m), starting a minute from now")
|
|
94
|
+
.option("--every <interval>", "how often: 5m, 90s, 1h — a minute or more", interval)
|
|
95
|
+
.action(async (options) => {
|
|
96
|
+
const config = updateConfig((current) => ({ ...current, auto: { enabled: true, every: options.every ?? autoState(current).every } }), env);
|
|
97
|
+
await applyAuto(env, autoState(config));
|
|
98
|
+
streams.data(`memo import runs every ${autoState(config).every}.\n`);
|
|
99
|
+
});
|
|
100
|
+
auto
|
|
101
|
+
.command("off")
|
|
102
|
+
.description("Stop importing on a timer and remove the timer")
|
|
103
|
+
.action(async () => {
|
|
104
|
+
const config = updateConfig((current) => ({ ...current, auto: { ...current.auto, enabled: false } }), env);
|
|
105
|
+
await applyAuto(env, autoState(config));
|
|
106
|
+
streams.data("memo import no longer runs on a timer.\n");
|
|
107
|
+
});
|
|
108
|
+
auto
|
|
109
|
+
.command("status")
|
|
110
|
+
.description("Whether the timer is on, its interval, when it runs next and how the last run ended")
|
|
111
|
+
.option("--json", "print JSON")
|
|
112
|
+
.action(async (options) => {
|
|
113
|
+
const state = autoState(loadConfig(env));
|
|
114
|
+
const show = async (unit, properties) => Object.fromEntries((await run("show", unit, "--property", properties).catch(() => ""))
|
|
115
|
+
.split("\n")
|
|
116
|
+
.filter(Boolean)
|
|
117
|
+
.map((line) => [line.slice(0, line.indexOf("=")), line.slice(line.indexOf("=") + 1)]));
|
|
118
|
+
const timer = await show(`${UNIT}.timer`, "ActiveState");
|
|
119
|
+
const service = await show(`${UNIT}.service`, "Result");
|
|
120
|
+
// list-timers gives wall-clock times for every kind of timer; `show` leaves them empty for these.
|
|
121
|
+
const listed = JSON.parse((await run("list-timers", `${UNIT}.timer`, "--output=json").catch(() => "[]")) || "[]")[0];
|
|
122
|
+
const when = (micros) => typeof micros === "number" && micros > 0 ? new Date(micros / 1000).toISOString() : null;
|
|
123
|
+
const status = {
|
|
124
|
+
...state,
|
|
125
|
+
timer: timer.ActiveState ?? "unknown",
|
|
126
|
+
next: when(listed?.next),
|
|
127
|
+
last: when(listed?.last),
|
|
128
|
+
lastResult: service.Result || null,
|
|
129
|
+
};
|
|
130
|
+
if (options.json)
|
|
131
|
+
createRenderer({ format: "json", color: false, streams }).result(status);
|
|
132
|
+
else
|
|
133
|
+
streams.data(`${status.enabled ? `on, every ${status.every}` : "off"} — timer ${status.timer}` +
|
|
134
|
+
(status.next ? `, next ${status.next}` : "") +
|
|
135
|
+
(status.last ? `, last ${status.last} (${status.lastResult ?? "?"})` : "") +
|
|
136
|
+
"\n");
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=auto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto.js","sourceRoot":"","sources":["../../src/run/auto.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Streams } from "@leemour/cli-core";
|
|
2
|
+
import type { Command } from "commander";
|
|
3
|
+
import { type ImportResult } from "../mail/import.js";
|
|
4
|
+
import { type NotesEmbedded } from "../notes/embed.js";
|
|
5
|
+
import { type NotesImport } from "../notes/import.js";
|
|
6
|
+
export interface ImportRun {
|
|
7
|
+
notes: (NotesImport & {
|
|
8
|
+
embedded?: NotesEmbedded;
|
|
9
|
+
})[];
|
|
10
|
+
mail: ImportResult[];
|
|
11
|
+
/** A source that failed; the others still ran. */
|
|
12
|
+
failed: {
|
|
13
|
+
source: string;
|
|
14
|
+
reason: string;
|
|
15
|
+
}[];
|
|
16
|
+
}
|
|
17
|
+
export declare const importCommand: (program: Command, streams: Streams, env: NodeJS.ProcessEnv) => void;
|
|
18
|
+
//# sourceMappingURL=import.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../src/run/import.ts"],"names":[],"mappings":"AACA,OAAO,EAA0C,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAGxF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAGxC,OAAO,EAAE,KAAK,YAAY,EAAc,MAAM,mBAAmB,CAAA;AACjE,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACpE,OAAO,EAAe,KAAK,WAAW,EAAY,MAAM,oBAAoB,CAAA;AAM5E,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,CAAC,WAAW,GAAG;QAAE,QAAQ,CAAC,EAAE,aAAa,CAAA;KAAE,CAAC,EAAE,CAAA;IACrD,IAAI,EAAE,YAAY,EAAE,CAAA;IACpB,kDAAkD;IAClD,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC7C;AAuBD,eAAO,MAAM,aAAa,YAAa,OAAO,WAAW,OAAO,OAAO,MAAM,CAAC,UAAU,KAAG,IAgE1F,CAAA"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { CliError, createRenderer, resolvePaths } from "@leemour/cli-core";
|
|
3
|
+
import { releaseLock, takeLock } from "@leemour/cli-messaging/background";
|
|
4
|
+
import { openStore } from "@leemour/cli-messaging/store";
|
|
5
|
+
import { loadConfig } from "../config.js";
|
|
6
|
+
import { himalaya } from "../mail/himalaya.js";
|
|
7
|
+
import { importMail } from "../mail/import.js";
|
|
8
|
+
import { embedChanged } from "../notes/embed.js";
|
|
9
|
+
import { importNotes, notesKey } from "../notes/import.js";
|
|
10
|
+
import { applyAuto, autoState } from "./auto.js";
|
|
11
|
+
const DAY = 86_400_000;
|
|
12
|
+
const MAIL_WINDOW_DAYS = 30;
|
|
13
|
+
const text = (run) => [
|
|
14
|
+
...run.notes.map((n) => `notes ${n.folder}: ${n.notes} notes, ${n.changed} changed, ${n.deleted} gone` +
|
|
15
|
+
(n.embedded === undefined
|
|
16
|
+
? ""
|
|
17
|
+
: n.embedded.notEmbedded !== undefined
|
|
18
|
+
? ` — not embedded: ${n.embedded.notEmbedded}`
|
|
19
|
+
: `, ${n.embedded.chunks} chunks embedded${n.embedded.left ? ", more next run" : ""}`) +
|
|
20
|
+
(n.deletionsSkipped === undefined ? "" : ` — deletions skipped: ${n.deletionsSkipped}`)),
|
|
21
|
+
...run.mail.map((m) => `mail ${m.account}: ${m.listed} listed, ${m.saved} new, ${m.deleted} gone` +
|
|
22
|
+
(m.complete ? "" : " — more to read next run") +
|
|
23
|
+
(m.deletionsSkipped === undefined ? "" : ` — deletions skipped: ${m.deletionsSkipped}`)),
|
|
24
|
+
...run.failed.map(({ source, reason }) => `failed ${source}: ${reason}`),
|
|
25
|
+
].join("\n") || "Nothing to import: set notes.folders or mail.accounts in the config.";
|
|
26
|
+
export const importCommand = (program, streams, env) => {
|
|
27
|
+
program
|
|
28
|
+
.command("import")
|
|
29
|
+
.description("Bring notes and mail up to date in the shared store — only what changed; the timer runs this")
|
|
30
|
+
.option("--notes", "notes only")
|
|
31
|
+
.option("--mail", "mail only")
|
|
32
|
+
.option("--no-embed", "build notes for search but skip embedding them for meaning this run")
|
|
33
|
+
.option("--json", "print JSON")
|
|
34
|
+
.action(async (options) => {
|
|
35
|
+
const both = !options.notes && !options.mail;
|
|
36
|
+
const config = loadConfig(env);
|
|
37
|
+
if (env.MEMO_AUTO === "1") {
|
|
38
|
+
await applyAuto(env, autoState(config));
|
|
39
|
+
if (!autoState(config).enabled)
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const lock = join(resolvePaths({ appName: "cli-memo", prefix: "MEMO", env }).state, "import.lock");
|
|
43
|
+
const holder = takeLock(lock, { pid: process.pid, startedAt: new Date().toISOString() });
|
|
44
|
+
if (holder !== undefined)
|
|
45
|
+
throw new CliError("rate_limited", `an import is already running (process ${holder.pid}); try again when it ends`);
|
|
46
|
+
const store = await openStore({ env });
|
|
47
|
+
const run = { notes: [], mail: [], failed: [] };
|
|
48
|
+
const attempt = async (source, work) => {
|
|
49
|
+
try {
|
|
50
|
+
await work();
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
run.failed.push({ source, reason: error instanceof Error ? error.message : String(error) });
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
try {
|
|
57
|
+
if (both || options.notes) {
|
|
58
|
+
for (const folder of config.notes?.folders ?? [])
|
|
59
|
+
await attempt(`notes ${folder}`, async () => {
|
|
60
|
+
const imported = await importNotes(store, folder, { ignore: config.notes?.ignore ?? [] });
|
|
61
|
+
const embedded = await embedChanged(store, notesKey(folder), imported.chats, {
|
|
62
|
+
embed: options.embed && config.notes?.embed !== false,
|
|
63
|
+
env,
|
|
64
|
+
});
|
|
65
|
+
run.notes.push({ ...imported, embedded });
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (both || options.mail) {
|
|
69
|
+
for (const account of config.mail?.accounts ?? [])
|
|
70
|
+
await attempt(`mail ${account.name}`, async () => {
|
|
71
|
+
run.mail.push(await importMail({
|
|
72
|
+
store,
|
|
73
|
+
run: himalaya(env),
|
|
74
|
+
account,
|
|
75
|
+
since: new Date(Date.now() - MAIL_WINDOW_DAYS * DAY),
|
|
76
|
+
}));
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
await store.close();
|
|
82
|
+
releaseLock(lock, process.pid);
|
|
83
|
+
}
|
|
84
|
+
if (options.json)
|
|
85
|
+
createRenderer({ format: "json", color: false, streams }).result(run);
|
|
86
|
+
else
|
|
87
|
+
streams.data(`${text(run)}\n`);
|
|
88
|
+
if (run.failed.length > 0)
|
|
89
|
+
process.exitCode = 1;
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import.js","sourceRoot":"","sources":["../../src/run/import.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AccountKey, MessageStore } from "@leemour/cli-messaging/store";
|
|
2
|
+
export interface Gone {
|
|
3
|
+
deleted: number;
|
|
4
|
+
/** Why stored entries missing at the source were left alone, when they were. */
|
|
5
|
+
skipped?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* What the store holds of this account, from `since` on, and the source no longer lists, was deleted
|
|
9
|
+
* there: its text goes. Wrongly dropping text cannot be undone and a missed deletion is caught on the
|
|
10
|
+
* next run, so an unusually large share missing is refused rather than applied — an unmounted folder or
|
|
11
|
+
* a half-answered listing looks exactly like that.
|
|
12
|
+
*/
|
|
13
|
+
export declare const dropMissing: (store: MessageStore, key: AccountKey, present: Set<string>, since?: string) => Promise<Gone>;
|
|
14
|
+
//# sourceMappingURL=gone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gone.d.ts","sourceRoot":"","sources":["../../src/store/gone.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAM5E,MAAM,WAAW,IAAI;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,UACf,YAAY,OACd,UAAU,WACN,GAAG,CAAC,MAAM,CAAC,UACZ,MAAM,KACb,OAAO,CAAC,IAAI,CA+Bd,CAAA"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const PAGE = 500;
|
|
2
|
+
const MAX_DELETED_SHARE = 0.2;
|
|
3
|
+
const MIN_DELETIONS_CAPPED = 5;
|
|
4
|
+
/**
|
|
5
|
+
* What the store holds of this account, from `since` on, and the source no longer lists, was deleted
|
|
6
|
+
* there: its text goes. Wrongly dropping text cannot be undone and a missed deletion is caught on the
|
|
7
|
+
* next run, so an unusually large share missing is refused rather than applied — an unmounted folder or
|
|
8
|
+
* a half-answered listing looks exactly like that.
|
|
9
|
+
*/
|
|
10
|
+
export const dropMissing = async (store, key, present, since) => {
|
|
11
|
+
const stored = [];
|
|
12
|
+
for (let offset = 0;; offset += PAGE) {
|
|
13
|
+
const chats = await store.chats(key, { limit: PAGE, offset });
|
|
14
|
+
for (const chat of chats.items) {
|
|
15
|
+
if (since !== undefined && chat.lastMessageAt !== null && chat.lastMessageAt < since)
|
|
16
|
+
continue;
|
|
17
|
+
for (let before;;) {
|
|
18
|
+
const page = await store.messages(key, chat.id, {
|
|
19
|
+
limit: PAGE,
|
|
20
|
+
...(since === undefined ? {} : { since }),
|
|
21
|
+
...(before === undefined ? {} : { before }),
|
|
22
|
+
});
|
|
23
|
+
stored.push(...page.items.map(({ id }) => ({ chatId: chat.id, id })));
|
|
24
|
+
before = page.items[0]?.id;
|
|
25
|
+
if (!page.hasMore || before === undefined)
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (!chats.hasMore)
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
const gone = stored.filter(({ id }) => !present.has(id));
|
|
33
|
+
if (gone.length === 0)
|
|
34
|
+
return { deleted: 0 };
|
|
35
|
+
if (gone.length >= MIN_DELETIONS_CAPPED && gone.length > stored.length * MAX_DELETED_SHARE)
|
|
36
|
+
return {
|
|
37
|
+
deleted: 0,
|
|
38
|
+
skipped: `${gone.length} of ${stored.length} stored entries are missing at the source — too many to trust; nothing deleted`,
|
|
39
|
+
};
|
|
40
|
+
const byChat = new Map();
|
|
41
|
+
for (const { chatId, id } of gone)
|
|
42
|
+
byChat.set(chatId, [...(byChat.get(chatId) ?? []), id]);
|
|
43
|
+
let deleted = 0;
|
|
44
|
+
for (const [chatId, ids] of byChat)
|
|
45
|
+
deleted += await store.markDeleted(key, ids, { chatId });
|
|
46
|
+
return { deleted };
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=gone.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gone.js","sourceRoot":"","sources":["../../src/store/gone.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,UAAU,CAAA"}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leemour/cli-memo",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Context about people across messengers, notes and mail, from a local store",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Viacheslav Ptsarev",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/leemour/cli-memo.git"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": "^22.16.0 || >=24"
|
|
14
|
+
},
|
|
15
|
+
"packageManager": "pnpm@11.20.0",
|
|
16
|
+
"bin": {
|
|
17
|
+
"memo": "dist/bin/memo.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc --build",
|
|
26
|
+
"typecheck": "tsc --build --force --emitDeclarationOnly && tsc -p tsconfig.test.json",
|
|
27
|
+
"lint": "biome check .",
|
|
28
|
+
"lint:fix": "biome check --write .",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest",
|
|
31
|
+
"test:coverage": "vitest run --coverage",
|
|
32
|
+
"release:check": "pnpm lint && pnpm typecheck && pnpm test && pnpm build && pnpm secrets:scan",
|
|
33
|
+
"prepare": "lefthook install || true",
|
|
34
|
+
"secrets:scan": "gitleaks git --no-banner --redact .",
|
|
35
|
+
"prepublishOnly": "pnpm build"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@leemour/cli-core": "0.17.1",
|
|
39
|
+
"@leemour/cli-messaging": "0.153.0",
|
|
40
|
+
"commander": "15.0.0",
|
|
41
|
+
"valibot": "1.5.0",
|
|
42
|
+
"yaml": "2.9.1"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@biomejs/biome": "^2.5.14",
|
|
46
|
+
"@types/node": "^22.20.4",
|
|
47
|
+
"@vitest/coverage-v8": "5.0.2",
|
|
48
|
+
"lefthook": "^2.1.14",
|
|
49
|
+
"typescript": "^7.0.2",
|
|
50
|
+
"vitest": "^5.0.2"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
}
|
|
55
|
+
}
|