plaud-notes-to-notion 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/LICENSE +21 -0
- package/README.md +102 -0
- package/bin/plaud-notes-to-notion.js +16 -0
- package/dist/card.d.ts +30 -0
- package/dist/card.js +287 -0
- package/dist/card.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +266 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +41 -0
- package/dist/config.js +85 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.d.ts +52 -0
- package/dist/constants.js +53 -0
- package/dist/constants.js.map +1 -0
- package/dist/daemon.d.ts +13 -0
- package/dist/daemon.js +182 -0
- package/dist/daemon.js.map +1 -0
- package/dist/format.d.ts +36 -0
- package/dist/format.js +108 -0
- package/dist/format.js.map +1 -0
- package/dist/ingest.d.ts +49 -0
- package/dist/ingest.js +205 -0
- package/dist/ingest.js.map +1 -0
- package/dist/llm.d.ts +65 -0
- package/dist/llm.js +177 -0
- package/dist/llm.js.map +1 -0
- package/dist/lock.d.ts +26 -0
- package/dist/lock.js +134 -0
- package/dist/lock.js.map +1 -0
- package/dist/log.d.ts +14 -0
- package/dist/log.js +41 -0
- package/dist/log.js.map +1 -0
- package/dist/notion.d.ts +86 -0
- package/dist/notion.js +254 -0
- package/dist/notion.js.map +1 -0
- package/dist/paths.d.ts +11 -0
- package/dist/paths.js +51 -0
- package/dist/paths.js.map +1 -0
- package/dist/plaud/callback.d.ts +19 -0
- package/dist/plaud/callback.js +162 -0
- package/dist/plaud/callback.js.map +1 -0
- package/dist/plaud/client.d.ts +55 -0
- package/dist/plaud/client.js +246 -0
- package/dist/plaud/client.js.map +1 -0
- package/dist/plaud/constants.d.ts +18 -0
- package/dist/plaud/constants.js +19 -0
- package/dist/plaud/constants.js.map +1 -0
- package/dist/plaud/errors.d.ts +10 -0
- package/dist/plaud/errors.js +32 -0
- package/dist/plaud/errors.js.map +1 -0
- package/dist/plaud/login.d.ts +17 -0
- package/dist/plaud/login.js +89 -0
- package/dist/plaud/login.js.map +1 -0
- package/dist/plaud/oauth.d.ts +21 -0
- package/dist/plaud/oauth.js +197 -0
- package/dist/plaud/oauth.js.map +1 -0
- package/dist/plaud/pkce.d.ts +3 -0
- package/dist/plaud/pkce.js +11 -0
- package/dist/plaud/pkce.js.map +1 -0
- package/dist/plaud/session.d.ts +23 -0
- package/dist/plaud/session.js +77 -0
- package/dist/plaud/session.js.map +1 -0
- package/dist/plaud/types.d.ts +21 -0
- package/dist/plaud/types.js +2 -0
- package/dist/plaud/types.js.map +1 -0
- package/dist/redact.d.ts +3 -0
- package/dist/redact.js +37 -0
- package/dist/redact.js.map +1 -0
- package/dist/runtime.d.ts +44 -0
- package/dist/runtime.js +94 -0
- package/dist/runtime.js.map +1 -0
- package/dist/scrub.d.ts +17 -0
- package/dist/scrub.js +124 -0
- package/dist/scrub.js.map +1 -0
- package/dist/secrets.d.ts +59 -0
- package/dist/secrets.js +220 -0
- package/dist/secrets.js.map +1 -0
- package/dist/ssn.d.ts +33 -0
- package/dist/ssn.js +170 -0
- package/dist/ssn.js.map +1 -0
- package/dist/state.d.ts +46 -0
- package/dist/state.js +23 -0
- package/dist/state.js.map +1 -0
- package/dist/sync.d.ts +60 -0
- package/dist/sync.js +190 -0
- package/dist/sync.js.map +1 -0
- package/examples/com.plaud-notes-to-notion.plist +34 -0
- package/examples/install-launchagent.sh +16 -0
- package/package.json +37 -0
package/dist/daemon.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { loadConfig } from "./config.js";
|
|
3
|
+
import { relogin, RELLOGIN_NOTION } from "./constants.js";
|
|
4
|
+
import { runIngest } from "./ingest.js";
|
|
5
|
+
import { createJobLock } from "./lock.js";
|
|
6
|
+
import { NotionError } from "./notion.js";
|
|
7
|
+
import { getConfigPath, getLockFilePath } from "./paths.js";
|
|
8
|
+
import { isAuthExpiredError, isTransportError } from "./plaud/errors.js";
|
|
9
|
+
import { safeErrorMessage } from "./redact.js";
|
|
10
|
+
import { buildProfileRuntime, notifyMac, readStatus, SetupError, updateStatus } from "./runtime.js";
|
|
11
|
+
import { loadState, pruneState, saveState } from "./state.js";
|
|
12
|
+
import { runDeletionSync, runSync } from "./sync.js";
|
|
13
|
+
const DEEP_SCAN_EVERY_MS = 10 * 60_000;
|
|
14
|
+
const ALERT_EVERY_MS = 6 * 3_600_000;
|
|
15
|
+
const RUNTIME_RETRY_MS = 5 * 60_000;
|
|
16
|
+
function describe(err, credentials) {
|
|
17
|
+
if (isAuthExpiredError(err)) {
|
|
18
|
+
return { message: relogin(credentials), auth: true };
|
|
19
|
+
}
|
|
20
|
+
if (err instanceof NotionError && err.unauthorized) {
|
|
21
|
+
return { message: RELLOGIN_NOTION(credentials), auth: true };
|
|
22
|
+
}
|
|
23
|
+
if (err instanceof SetupError) {
|
|
24
|
+
return { message: err.message, auth: true };
|
|
25
|
+
}
|
|
26
|
+
if (isTransportError(err)) {
|
|
27
|
+
const c = err.cause;
|
|
28
|
+
const detail = c ? ` — ${[c.name, c.code, c.message].filter(Boolean).join(" ")}` : "";
|
|
29
|
+
return { message: `Network problem (${safeErrorMessage(err)}${safeErrorMessage(detail)}); retrying next cycle.`, auth: false };
|
|
30
|
+
}
|
|
31
|
+
return { message: safeErrorMessage(err), auth: false };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Long-running LaunchAgent loop. Per enabled profile: ingest every pollSeconds
|
|
35
|
+
* (deep scan every 10 min), Summary→Transcript sync every syncSeconds.
|
|
36
|
+
* Re-reads config.json when it changes, so turning Tim on needs no restart.
|
|
37
|
+
*/
|
|
38
|
+
export async function runDaemon(options) {
|
|
39
|
+
const env = options.env || process.env;
|
|
40
|
+
const log = options.log;
|
|
41
|
+
const lock = createJobLock({ lockFile: getLockFilePath(env), log: (m) => log.warn(m) });
|
|
42
|
+
if (!lock.acquire()) {
|
|
43
|
+
log.warn("Another plaud-notes-to-notion daemon holds the lock; exiting.");
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
const stop = () => lock.release();
|
|
47
|
+
process.once("SIGTERM", () => {
|
|
48
|
+
stop();
|
|
49
|
+
process.exit(0);
|
|
50
|
+
});
|
|
51
|
+
process.once("SIGINT", () => {
|
|
52
|
+
stop();
|
|
53
|
+
process.exit(0);
|
|
54
|
+
});
|
|
55
|
+
let config = loadConfig(env);
|
|
56
|
+
let configMtime = fs.statSync(getConfigPath(env)).mtimeMs;
|
|
57
|
+
const timers = new Map();
|
|
58
|
+
log.info(`Daemon started (poll ${config.pollSeconds}s, sync ${config.syncSeconds}s, model ${config.llmModel}/${config.llmEffort}).`);
|
|
59
|
+
const reportError = (name, credentials, err) => {
|
|
60
|
+
const { message, auth } = describe(err, credentials);
|
|
61
|
+
const now = new Date();
|
|
62
|
+
const prev = readStatus(env)[name];
|
|
63
|
+
const patch = { lastError: message, lastErrorAt: now.toISOString() };
|
|
64
|
+
if (auth && (!prev?.alertedAt || now.getTime() - Date.parse(prev.alertedAt) > ALERT_EVERY_MS)) {
|
|
65
|
+
notifyMac("Plaud Notes to Notion", message);
|
|
66
|
+
patch.alertedAt = now.toISOString();
|
|
67
|
+
}
|
|
68
|
+
updateStatus(name, patch, env);
|
|
69
|
+
log.error(`[${name}] ${message}`);
|
|
70
|
+
return auth;
|
|
71
|
+
};
|
|
72
|
+
while (!options.signal?.aborted) {
|
|
73
|
+
try {
|
|
74
|
+
const mtime = fs.statSync(getConfigPath(env)).mtimeMs;
|
|
75
|
+
if (mtime !== configMtime) {
|
|
76
|
+
config = loadConfig(env);
|
|
77
|
+
configMtime = mtime;
|
|
78
|
+
for (const t of timers.values()) {
|
|
79
|
+
t.runtime = undefined;
|
|
80
|
+
}
|
|
81
|
+
log.info("Config changed; reloaded.");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
log.error(`Config reload failed; keeping previous config: ${safeErrorMessage(err)}`);
|
|
86
|
+
}
|
|
87
|
+
for (const [name, profile] of Object.entries(config.profiles)) {
|
|
88
|
+
if (!profile.enabled || (!profile.ingest && !profile.syncTranscripts)) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const now = Date.now();
|
|
92
|
+
const t = timers.get(name) || { nextPoll: now, nextSync: now, nextDeep: now, runtimeRetryAt: 0 };
|
|
93
|
+
timers.set(name, t);
|
|
94
|
+
if (!t.runtime) {
|
|
95
|
+
if (now < t.runtimeRetryAt) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
t.runtime = await buildProfileRuntime({
|
|
100
|
+
name,
|
|
101
|
+
profile,
|
|
102
|
+
config,
|
|
103
|
+
store: options.store,
|
|
104
|
+
env,
|
|
105
|
+
needPlaud: profile.ingest,
|
|
106
|
+
needLabeler: profile.ingest
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
reportError(name, profile.credentials, err);
|
|
111
|
+
t.runtimeRetryAt = now + RUNTIME_RETRY_MS;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const rt = t.runtime;
|
|
116
|
+
const state = loadState(name, env);
|
|
117
|
+
const save = () => saveState(name, state, env);
|
|
118
|
+
const plog = (m) => log.info(m);
|
|
119
|
+
if (profile.ingest && now >= t.nextPoll) {
|
|
120
|
+
const deep = now >= t.nextDeep;
|
|
121
|
+
t.nextPoll = now + config.pollSeconds * 1000;
|
|
122
|
+
if (deep) {
|
|
123
|
+
t.nextDeep = now + DEEP_SCAN_EVERY_MS;
|
|
124
|
+
}
|
|
125
|
+
updateStatus(name, { lastPollAt: new Date().toISOString() }, env);
|
|
126
|
+
try {
|
|
127
|
+
const r = await runIngest({
|
|
128
|
+
profile: name,
|
|
129
|
+
owner: profile.owner,
|
|
130
|
+
startAfter: new Date(profile.startAfter),
|
|
131
|
+
plaud: rt.plaud,
|
|
132
|
+
notion: rt.notion,
|
|
133
|
+
labeler: rt.labeler,
|
|
134
|
+
state,
|
|
135
|
+
save,
|
|
136
|
+
log: plog
|
|
137
|
+
}, { deep });
|
|
138
|
+
pruneState(state, new Date());
|
|
139
|
+
save();
|
|
140
|
+
const patch = { lastPollOkAt: new Date().toISOString(), pending: r.pending };
|
|
141
|
+
if (r.written.length) {
|
|
142
|
+
patch.lastWritten = { title: r.written.at(-1)?.title, at: new Date().toISOString() };
|
|
143
|
+
}
|
|
144
|
+
if (!r.errors.length) {
|
|
145
|
+
patch.lastError = undefined;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
for (const e of r.errors) {
|
|
149
|
+
log.error(`[${name}] ${e}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
updateStatus(name, patch, env);
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
if (reportError(name, profile.credentials, err)) {
|
|
156
|
+
t.runtime = undefined;
|
|
157
|
+
t.runtimeRetryAt = Date.now() + RUNTIME_RETRY_MS;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (profile.syncTranscripts && now >= t.nextSync) {
|
|
163
|
+
t.nextSync = now + config.syncSeconds * 1000;
|
|
164
|
+
try {
|
|
165
|
+
await runSync({ profile: name, notion: rt.notion, state, save, log: plog });
|
|
166
|
+
await runDeletionSync({ profile: name, notion: rt.notion, state, save, log: plog });
|
|
167
|
+
updateStatus(name, { lastSyncOkAt: new Date().toISOString() }, env);
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
if (reportError(name, profile.credentials, err)) {
|
|
171
|
+
t.runtime = undefined;
|
|
172
|
+
t.runtimeRetryAt = Date.now() + RUNTIME_RETRY_MS;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
178
|
+
}
|
|
179
|
+
stop();
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
|
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { PlaudSegment } from "./plaud/client.js";
|
|
2
|
+
/**
|
|
3
|
+
* Summary cleanup ported from the Zap (steps 3–5), in the same order:
|
|
4
|
+
* 3. drop the literal "PLAUD NOTE" banner
|
|
5
|
+
* 4. straight double quotes → typographic quotes (the Zap turned every `"`
|
|
6
|
+
* into a closing `”`; this pairs them as “…”)
|
|
7
|
+
* 5. drop Plaud image embeds (``), `---` dividers and blank
|
|
8
|
+
* lines, and split `> **Label:** text` into a bold label + quote.
|
|
9
|
+
*/
|
|
10
|
+
export declare function cleanSummaryMarkdown(markdown: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Consecutive `>` lines are one quote in the Zap's output (one Notion quote
|
|
13
|
+
* block with line breaks). Notion's markdown API makes a block per line, so
|
|
14
|
+
* join them with `<br>`; list lines inside a quote become the quote's
|
|
15
|
+
* children (the Zap nested them, but under the previous to-do by mistake).
|
|
16
|
+
*/
|
|
17
|
+
export declare function mergeQuoteRuns(markdown: string): string;
|
|
18
|
+
/** Opening quote after start/space/bracket/dash, closing quote everywhere else. */
|
|
19
|
+
export declare function smartQuotes(text: string): string;
|
|
20
|
+
export declare function formatOffset(ms: number): string;
|
|
21
|
+
/**
|
|
22
|
+
* Transcript text exactly as the Zap wrote it (Plaud's "Markdown" transcript):
|
|
23
|
+
* `Speaker 1 00:00:01` (start rounded to the second) then the utterance, one
|
|
24
|
+
* line each, no merging.
|
|
25
|
+
*/
|
|
26
|
+
export declare function formatTranscript(segments: PlaudSegment[]): string;
|
|
27
|
+
/** Notion `Recorded`: recording start, truncated to the minute, as a UTC instant. */
|
|
28
|
+
export declare function recordedMinute(startAt: Date): string;
|
|
29
|
+
/** Meeting title: Plaud's own title minus its leading `MM-DD ` date (Peter 2026-09-26). */
|
|
30
|
+
export declare function meetingTitle(plaudName: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Participants as Notion multi-select options: owner and generic diarization
|
|
33
|
+
* labels removed, commas stripped (Notion rejects them), duplicates dropped.
|
|
34
|
+
*/
|
|
35
|
+
export declare function cleanParticipants(names: string[], owner: string): string[];
|
|
36
|
+
export declare function cleanTitle(title: string): string;
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Summary cleanup ported from the Zap (steps 3–5), in the same order:
|
|
3
|
+
* 3. drop the literal "PLAUD NOTE" banner
|
|
4
|
+
* 4. straight double quotes → typographic quotes (the Zap turned every `"`
|
|
5
|
+
* into a closing `”`; this pairs them as “…”)
|
|
6
|
+
* 5. drop Plaud image embeds (``), `---` dividers and blank
|
|
7
|
+
* lines, and split `> **Label:** text` into a bold label + quote.
|
|
8
|
+
*/
|
|
9
|
+
export function cleanSummaryMarkdown(markdown) {
|
|
10
|
+
let t = markdown.replace(/\r\n?/g, "\n");
|
|
11
|
+
t = t.split("PLAUD NOTE").join("");
|
|
12
|
+
t = smartQuotes(t);
|
|
13
|
+
t = t.replace(/!\[[^\]]*\]\([ \t]*\n?[ \t]*permanent\/[^)]*\)\n*/g, "");
|
|
14
|
+
t = t.replace(/\n-{3,}\n/g, "\n");
|
|
15
|
+
t = t.replace(/\n[ \t]*\n+/g, "\n");
|
|
16
|
+
t = t.replace(/^> \*\*(.+?):\*\*[ \t]*(.*)$/gm, (_m, label, rest) => rest && rest.trim().length > 0 ? `**${label}:**\n> ${rest}` : `**${label}:**`);
|
|
17
|
+
return mergeQuoteRuns(t.trim());
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Consecutive `>` lines are one quote in the Zap's output (one Notion quote
|
|
21
|
+
* block with line breaks). Notion's markdown API makes a block per line, so
|
|
22
|
+
* join them with `<br>`; list lines inside a quote become the quote's
|
|
23
|
+
* children (the Zap nested them, but under the previous to-do by mistake).
|
|
24
|
+
*/
|
|
25
|
+
export function mergeQuoteRuns(markdown) {
|
|
26
|
+
const lines = markdown.split("\n");
|
|
27
|
+
const out = [];
|
|
28
|
+
let i = 0;
|
|
29
|
+
while (i < lines.length) {
|
|
30
|
+
if (!/^>/.test(lines[i])) {
|
|
31
|
+
out.push(lines[i]);
|
|
32
|
+
i++;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const text = [];
|
|
36
|
+
const children = [];
|
|
37
|
+
while (i < lines.length && /^>/.test(lines[i])) {
|
|
38
|
+
const body = lines[i].replace(/^>[ \t]?/, "");
|
|
39
|
+
if (children.length || (text.length && /^(?:\d+\.|[-*+])\s+/.test(body))) {
|
|
40
|
+
children.push(`\t${body}`);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
text.push(body);
|
|
44
|
+
}
|
|
45
|
+
i++;
|
|
46
|
+
}
|
|
47
|
+
out.push(`> ${text.join("<br>")}`, ...children);
|
|
48
|
+
}
|
|
49
|
+
return out.join("\n");
|
|
50
|
+
}
|
|
51
|
+
/** Opening quote after start/space/bracket/dash, closing quote everywhere else. */
|
|
52
|
+
export function smartQuotes(text) {
|
|
53
|
+
return text.replace(/(^|[\s(\[{—–-])"/gm, "$1“").replace(/"/g, "”");
|
|
54
|
+
}
|
|
55
|
+
export function formatOffset(ms) {
|
|
56
|
+
const total = Math.max(0, Math.round(ms / 1000));
|
|
57
|
+
const h = Math.floor(total / 3600);
|
|
58
|
+
const m = Math.floor((total % 3600) / 60);
|
|
59
|
+
const s = total % 60;
|
|
60
|
+
return [h, m, s].map((n) => String(n).padStart(2, "0")).join(":");
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Transcript text exactly as the Zap wrote it (Plaud's "Markdown" transcript):
|
|
64
|
+
* `Speaker 1 00:00:01` (start rounded to the second) then the utterance, one
|
|
65
|
+
* line each, no merging.
|
|
66
|
+
*/
|
|
67
|
+
export function formatTranscript(segments) {
|
|
68
|
+
return segments.map((s) => `${s.speaker} ${formatOffset(s.startMs)}\n${s.text}`).join("\n");
|
|
69
|
+
}
|
|
70
|
+
/** Notion `Recorded`: recording start, truncated to the minute, as a UTC instant. */
|
|
71
|
+
export function recordedMinute(startAt) {
|
|
72
|
+
const d = new Date(startAt.getTime());
|
|
73
|
+
d.setUTCSeconds(0, 0);
|
|
74
|
+
return d.toISOString();
|
|
75
|
+
}
|
|
76
|
+
/** Meeting title: Plaud's own title minus its leading `MM-DD ` date (Peter 2026-09-26). */
|
|
77
|
+
export function meetingTitle(plaudName) {
|
|
78
|
+
const t = plaudName.replace(/^\d{2}-\d{2}\s+/, "").trim();
|
|
79
|
+
return t || "Untitled recording";
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Participants as Notion multi-select options: owner and generic diarization
|
|
83
|
+
* labels removed, commas stripped (Notion rejects them), duplicates dropped.
|
|
84
|
+
*/
|
|
85
|
+
export function cleanParticipants(names, owner) {
|
|
86
|
+
const ownerLc = owner.trim().toLowerCase();
|
|
87
|
+
const seen = new Set();
|
|
88
|
+
const out = [];
|
|
89
|
+
for (const raw of names) {
|
|
90
|
+
const name = raw.replace(/,/g, "").replace(/\s+/g, " ").trim().slice(0, 100);
|
|
91
|
+
const lc = name.toLowerCase();
|
|
92
|
+
if (!name || /^speaker\s*\d*$/i.test(name) || /^unknown/i.test(name)) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (lc === ownerLc) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (!seen.has(lc)) {
|
|
99
|
+
seen.add(lc);
|
|
100
|
+
out.push(name);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
export function cleanTitle(title) {
|
|
106
|
+
return title.replace(/\s+/g, " ").trim().slice(0, 300);
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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"}
|
package/dist/ingest.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Labeler } from "./llm.js";
|
|
2
|
+
import type { NotionStore } from "./notion.js";
|
|
3
|
+
import type { PlaudClient, PlaudRecording } from "./plaud/client.js";
|
|
4
|
+
import type { ProfileState } from "./state.js";
|
|
5
|
+
export interface IngestDeps {
|
|
6
|
+
profile: string;
|
|
7
|
+
owner: string;
|
|
8
|
+
startAfter: Date;
|
|
9
|
+
plaud: PlaudClient;
|
|
10
|
+
notion: NotionStore;
|
|
11
|
+
labeler: Labeler;
|
|
12
|
+
state: ProfileState;
|
|
13
|
+
save: () => void;
|
|
14
|
+
log: (msg: string) => void;
|
|
15
|
+
now?: () => Date;
|
|
16
|
+
dryRun?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface IngestResult {
|
|
19
|
+
checked: number;
|
|
20
|
+
written: Array<{
|
|
21
|
+
id: string;
|
|
22
|
+
title: string;
|
|
23
|
+
}>;
|
|
24
|
+
pending: number;
|
|
25
|
+
errors: string[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* One-time snapshot at cutover: every recording from the last WATCH_DAYS that was
|
|
29
|
+
* uploaded before startAfter. Ones Plaud has already finished belonged to the Zap
|
|
30
|
+
* ("baseline", never written). Ones still without a summary/transcript go on the
|
|
31
|
+
* slow watch list, so a summary generated later is still written — as the Zap did.
|
|
32
|
+
*/
|
|
33
|
+
export declare function runBaseline(deps: IngestDeps): Promise<{
|
|
34
|
+
baseline: number;
|
|
35
|
+
watching: number;
|
|
36
|
+
}>;
|
|
37
|
+
export interface PreparedRecording {
|
|
38
|
+
recorded: string;
|
|
39
|
+
summaryMarkdown: string;
|
|
40
|
+
transcriptText: string;
|
|
41
|
+
plaudTitle: string;
|
|
42
|
+
ssnRedactions: number;
|
|
43
|
+
cardRedactions: number;
|
|
44
|
+
}
|
|
45
|
+
/** Everything that happens to Plaud text before it leaves this Mac. */
|
|
46
|
+
export declare function prepareRecording(rec: PlaudRecording): PreparedRecording;
|
|
47
|
+
export declare function runIngest(deps: IngestDeps, options?: {
|
|
48
|
+
deep?: boolean;
|
|
49
|
+
}): Promise<IngestResult>;
|
package/dist/ingest.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { LLM_MAX_ATTEMPTS, PENDING_FAST_HOURS, TYPE_SUMMARY, TYPE_TRANSCRIPT, WATCH_DAYS } from "./constants.js";
|
|
2
|
+
import { cleanParticipants, cleanSummaryMarkdown, cleanTitle, formatTranscript, meetingTitle, recordedMinute } from "./format.js";
|
|
3
|
+
import { redactCardsAcross } from "./card.js";
|
|
4
|
+
import { redactSsnAcross } from "./ssn.js";
|
|
5
|
+
import { safeErrorMessage } from "./redact.js";
|
|
6
|
+
const DEEP_SCAN_PAGES = 15;
|
|
7
|
+
/**
|
|
8
|
+
* Files to look at this cycle, oldest upload first:
|
|
9
|
+
* - new files (uploaded at/after startAfter) not seen yet;
|
|
10
|
+
* - files still waiting on Plaud (no summary/transcript yet) — every cycle while
|
|
11
|
+
* fresh, only on deep scans once they are "slow" (older than PENDING_FAST_HOURS).
|
|
12
|
+
* Plaud lists by recording start, so a deep scan (every 10 min) walks back
|
|
13
|
+
* WATCH_DAYS to catch late uploads and summaries generated late on old recordings.
|
|
14
|
+
*/
|
|
15
|
+
async function candidates(deps, deep) {
|
|
16
|
+
const now = (deps.now || (() => new Date()))();
|
|
17
|
+
const floor = new Date(now.getTime() - WATCH_DAYS * 86_400_000);
|
|
18
|
+
const out = [];
|
|
19
|
+
const pages = deep ? DEEP_SCAN_PAGES : 1;
|
|
20
|
+
for (let page = 1; page <= pages; page++) {
|
|
21
|
+
const list = await deps.plaud.listFiles(page, 20);
|
|
22
|
+
for (const f of list) {
|
|
23
|
+
const uploaded = f.createdAt || f.startAt;
|
|
24
|
+
const st = deps.state.files[f.id];
|
|
25
|
+
if (st) {
|
|
26
|
+
if (st.status === "pending" && (deep || !st.slow)) {
|
|
27
|
+
out.push(f);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else if (uploaded && uploaded >= deps.startAfter) {
|
|
31
|
+
out.push(f);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const oldest = list.at(-1)?.startAt;
|
|
35
|
+
if (list.length < 20 || !oldest || oldest < floor) {
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return out.sort((a, b) => (a.createdAt?.getTime() ?? 0) - (b.createdAt?.getTime() ?? 0));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* One-time snapshot at cutover: every recording from the last WATCH_DAYS that was
|
|
43
|
+
* uploaded before startAfter. Ones Plaud has already finished belonged to the Zap
|
|
44
|
+
* ("baseline", never written). Ones still without a summary/transcript go on the
|
|
45
|
+
* slow watch list, so a summary generated later is still written — as the Zap did.
|
|
46
|
+
*/
|
|
47
|
+
export async function runBaseline(deps) {
|
|
48
|
+
const now = (deps.now || (() => new Date()))();
|
|
49
|
+
const floor = new Date(now.getTime() - WATCH_DAYS * 86_400_000);
|
|
50
|
+
let baseline = 0;
|
|
51
|
+
let watching = 0;
|
|
52
|
+
for (let page = 1; page <= DEEP_SCAN_PAGES; page++) {
|
|
53
|
+
const list = await deps.plaud.listFiles(page, 20);
|
|
54
|
+
for (const f of list) {
|
|
55
|
+
const uploaded = f.createdAt || f.startAt;
|
|
56
|
+
if (deps.state.files[f.id] || !uploaded || uploaded >= deps.startAfter) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const rec = await deps.plaud.getRecording(f.id);
|
|
60
|
+
const ready = Boolean(rec.summaryMarkdown && rec.segments.length > 0 && rec.startAt);
|
|
61
|
+
deps.state.files[f.id] = ready
|
|
62
|
+
? { status: "baseline", firstSeen: now.toISOString(), name: rec.name, llmAttempts: 0 }
|
|
63
|
+
: { status: "pending", slow: true, firstSeen: now.toISOString(), name: rec.name, llmAttempts: 0 };
|
|
64
|
+
ready ? baseline++ : watching++;
|
|
65
|
+
}
|
|
66
|
+
const oldest = list.at(-1)?.startAt;
|
|
67
|
+
if (list.length < 20 || !oldest || oldest < floor) {
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!deps.dryRun) {
|
|
72
|
+
deps.state.baselineAt = now.toISOString();
|
|
73
|
+
deps.save();
|
|
74
|
+
}
|
|
75
|
+
deps.log(`[${deps.profile}] baseline: ${baseline} recording(s) already handled before cutover; watching ${watching} still without a summary`);
|
|
76
|
+
return { baseline, watching };
|
|
77
|
+
}
|
|
78
|
+
/** A row we (or a crashed earlier attempt) already wrote: same minute, same Type, same title. */
|
|
79
|
+
function alreadyWritten(rows, type, title) {
|
|
80
|
+
return rows.find((r) => r.types.includes(type) && r.title.trim() === title.trim());
|
|
81
|
+
}
|
|
82
|
+
/** Everything that happens to Plaud text before it leaves this Mac. */
|
|
83
|
+
export function prepareRecording(rec) {
|
|
84
|
+
// Cards first: "last four of the card" belongs to the card, not the SSN.
|
|
85
|
+
const cards = redactCardsAcross({
|
|
86
|
+
summary: rec.summaryMarkdown || "",
|
|
87
|
+
transcript: formatTranscript(rec.segments),
|
|
88
|
+
title: rec.name
|
|
89
|
+
});
|
|
90
|
+
const redacted = redactSsnAcross(cards.texts);
|
|
91
|
+
return {
|
|
92
|
+
recorded: recordedMinute(rec.startAt),
|
|
93
|
+
summaryMarkdown: cleanSummaryMarkdown(redacted.texts.summary),
|
|
94
|
+
transcriptText: redacted.texts.transcript,
|
|
95
|
+
plaudTitle: redacted.texts.title,
|
|
96
|
+
ssnRedactions: redacted.count,
|
|
97
|
+
cardRedactions: cards.count
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async function processOne(deps, file, st, result) {
|
|
101
|
+
const now = (deps.now || (() => new Date()))();
|
|
102
|
+
const rec = await deps.plaud.getRecording(file.id);
|
|
103
|
+
st.name = rec.name;
|
|
104
|
+
if (!rec.summaryMarkdown || rec.segments.length === 0 || !rec.startAt) {
|
|
105
|
+
const waitedH = (now.getTime() - Date.parse(st.firstSeen)) / 3_600_000;
|
|
106
|
+
if (!st.slow && waitedH > PENDING_FAST_HOURS) {
|
|
107
|
+
st.slow = true;
|
|
108
|
+
deps.log(`[${deps.profile}] "${rec.name}" still has no ${!rec.summaryMarkdown ? "summary" : "transcript"} after ${PENDING_FAST_HOURS}h; checking it every 10 min instead`);
|
|
109
|
+
}
|
|
110
|
+
result.pending++;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const prepared = prepareRecording(rec);
|
|
114
|
+
st.recorded = prepared.recorded;
|
|
115
|
+
st.ssnRedactions = prepared.ssnRedactions;
|
|
116
|
+
st.cardRedactions = prepared.cardRedactions;
|
|
117
|
+
if (prepared.ssnRedactions > 0) {
|
|
118
|
+
deps.log(`[${deps.profile}] redacted ${prepared.ssnRedactions} SSN mention(s) in "${prepared.plaudTitle}"`);
|
|
119
|
+
}
|
|
120
|
+
if (prepared.cardRedactions > 0) {
|
|
121
|
+
deps.log(`[${deps.profile}] redacted ${prepared.cardRedactions} card detail(s) in "${prepared.plaudTitle}"`);
|
|
122
|
+
}
|
|
123
|
+
if (!st.title) {
|
|
124
|
+
const notionSchema = await deps.notion.schema();
|
|
125
|
+
try {
|
|
126
|
+
const labels = await deps.labeler.label({
|
|
127
|
+
owner: deps.owner,
|
|
128
|
+
plaudTitle: prepared.plaudTitle,
|
|
129
|
+
summaryMarkdown: prepared.summaryMarkdown,
|
|
130
|
+
transcriptText: prepared.transcriptText,
|
|
131
|
+
knownParticipants: notionSchema.participantOptions
|
|
132
|
+
});
|
|
133
|
+
st.participants = cleanParticipants(labels.participants, deps.owner);
|
|
134
|
+
st.labelSource = "llm";
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
st.llmAttempts++;
|
|
138
|
+
st.lastError = safeErrorMessage(err);
|
|
139
|
+
if (st.llmAttempts < LLM_MAX_ATTEMPTS) {
|
|
140
|
+
deps.log(`[${deps.profile}] participant labeling failed for "${prepared.plaudTitle}" (attempt ${st.llmAttempts}/${LLM_MAX_ATTEMPTS}): ${st.lastError}`);
|
|
141
|
+
result.pending++;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
st.participants = [];
|
|
145
|
+
st.labelSource = "fallback";
|
|
146
|
+
deps.log(`[${deps.profile}] participant labeling failed ${LLM_MAX_ATTEMPTS}x; writing "${prepared.plaudTitle}" with no participants`);
|
|
147
|
+
}
|
|
148
|
+
st.title = cleanTitle(meetingTitle(prepared.plaudTitle));
|
|
149
|
+
deps.save();
|
|
150
|
+
}
|
|
151
|
+
const title = st.title;
|
|
152
|
+
const participants = st.participants || [];
|
|
153
|
+
if (deps.dryRun) {
|
|
154
|
+
deps.log(`[${deps.profile}] DRY RUN would write "${title}" Recorded ${prepared.recorded} Participants ${JSON.stringify(participants)} ` +
|
|
155
|
+
`(summary ${prepared.summaryMarkdown.length} chars, transcript ${prepared.transcriptText.length} chars, SSN redactions ${prepared.ssnRedactions}, card redactions ${prepared.cardRedactions})`);
|
|
156
|
+
result.written.push({ id: file.id, title });
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const existing = await deps.notion.rowsAtMinute(prepared.recorded);
|
|
160
|
+
if (!st.summaryPageId) {
|
|
161
|
+
const found = alreadyWritten(existing, TYPE_SUMMARY, title);
|
|
162
|
+
st.summaryPageId = found
|
|
163
|
+
? found.id
|
|
164
|
+
: (await deps.notion.createSummary({ title, recorded: prepared.recorded, type: TYPE_SUMMARY, participants }, prepared.summaryMarkdown)).id;
|
|
165
|
+
deps.save();
|
|
166
|
+
}
|
|
167
|
+
if (!st.transcriptPageId) {
|
|
168
|
+
const found = alreadyWritten(existing, TYPE_TRANSCRIPT, title);
|
|
169
|
+
st.transcriptPageId = found
|
|
170
|
+
? found.id
|
|
171
|
+
: (await deps.notion.createTranscript({ title, recorded: prepared.recorded, type: TYPE_TRANSCRIPT, participants }, prepared.transcriptText)).id;
|
|
172
|
+
}
|
|
173
|
+
st.status = "done";
|
|
174
|
+
st.doneAt = now.toISOString();
|
|
175
|
+
delete st.lastError;
|
|
176
|
+
deps.save();
|
|
177
|
+
result.written.push({ id: file.id, title });
|
|
178
|
+
deps.log(`[${deps.profile}] wrote "${title}" (Recorded ${prepared.recorded}, participants ${JSON.stringify(participants)})`);
|
|
179
|
+
}
|
|
180
|
+
export async function runIngest(deps, options = {}) {
|
|
181
|
+
const now = (deps.now || (() => new Date()))();
|
|
182
|
+
const result = { checked: 0, written: [], pending: 0, errors: [] };
|
|
183
|
+
if (!deps.state.baselineAt) {
|
|
184
|
+
await runBaseline(deps);
|
|
185
|
+
}
|
|
186
|
+
for (const file of await candidates(deps, options.deep === true)) {
|
|
187
|
+
result.checked++;
|
|
188
|
+
const st = (deps.state.files[file.id] ||= { status: "pending", firstSeen: now.toISOString(), llmAttempts: 0 });
|
|
189
|
+
try {
|
|
190
|
+
await processOne(deps, file, st, result);
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
st.lastError = safeErrorMessage(err);
|
|
194
|
+
result.errors.push(`${file.name}: ${st.lastError}`);
|
|
195
|
+
deps.save();
|
|
196
|
+
// Auth problems affect every file; stop the cycle and let the daemon alert.
|
|
197
|
+
const e = err;
|
|
198
|
+
if (e.unauthorized || e.code === "AUTH_EXPIRED") {
|
|
199
|
+
throw err;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=ingest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest.js","sourceRoot":"","sources":["../src/ingest.ts"],"names":[],"mappings":"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"}
|