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.
Files changed (91) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/LICENSE +21 -0
  3. package/README.md +102 -0
  4. package/bin/plaud-notes-to-notion.js +16 -0
  5. package/dist/card.d.ts +30 -0
  6. package/dist/card.js +287 -0
  7. package/dist/card.js.map +1 -0
  8. package/dist/cli.d.ts +1 -0
  9. package/dist/cli.js +266 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/config.d.ts +41 -0
  12. package/dist/config.js +85 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/constants.d.ts +52 -0
  15. package/dist/constants.js +53 -0
  16. package/dist/constants.js.map +1 -0
  17. package/dist/daemon.d.ts +13 -0
  18. package/dist/daemon.js +182 -0
  19. package/dist/daemon.js.map +1 -0
  20. package/dist/format.d.ts +36 -0
  21. package/dist/format.js +108 -0
  22. package/dist/format.js.map +1 -0
  23. package/dist/ingest.d.ts +49 -0
  24. package/dist/ingest.js +205 -0
  25. package/dist/ingest.js.map +1 -0
  26. package/dist/llm.d.ts +65 -0
  27. package/dist/llm.js +177 -0
  28. package/dist/llm.js.map +1 -0
  29. package/dist/lock.d.ts +26 -0
  30. package/dist/lock.js +134 -0
  31. package/dist/lock.js.map +1 -0
  32. package/dist/log.d.ts +14 -0
  33. package/dist/log.js +41 -0
  34. package/dist/log.js.map +1 -0
  35. package/dist/notion.d.ts +86 -0
  36. package/dist/notion.js +254 -0
  37. package/dist/notion.js.map +1 -0
  38. package/dist/paths.d.ts +11 -0
  39. package/dist/paths.js +51 -0
  40. package/dist/paths.js.map +1 -0
  41. package/dist/plaud/callback.d.ts +19 -0
  42. package/dist/plaud/callback.js +162 -0
  43. package/dist/plaud/callback.js.map +1 -0
  44. package/dist/plaud/client.d.ts +55 -0
  45. package/dist/plaud/client.js +246 -0
  46. package/dist/plaud/client.js.map +1 -0
  47. package/dist/plaud/constants.d.ts +18 -0
  48. package/dist/plaud/constants.js +19 -0
  49. package/dist/plaud/constants.js.map +1 -0
  50. package/dist/plaud/errors.d.ts +10 -0
  51. package/dist/plaud/errors.js +32 -0
  52. package/dist/plaud/errors.js.map +1 -0
  53. package/dist/plaud/login.d.ts +17 -0
  54. package/dist/plaud/login.js +89 -0
  55. package/dist/plaud/login.js.map +1 -0
  56. package/dist/plaud/oauth.d.ts +21 -0
  57. package/dist/plaud/oauth.js +197 -0
  58. package/dist/plaud/oauth.js.map +1 -0
  59. package/dist/plaud/pkce.d.ts +3 -0
  60. package/dist/plaud/pkce.js +11 -0
  61. package/dist/plaud/pkce.js.map +1 -0
  62. package/dist/plaud/session.d.ts +23 -0
  63. package/dist/plaud/session.js +77 -0
  64. package/dist/plaud/session.js.map +1 -0
  65. package/dist/plaud/types.d.ts +21 -0
  66. package/dist/plaud/types.js +2 -0
  67. package/dist/plaud/types.js.map +1 -0
  68. package/dist/redact.d.ts +3 -0
  69. package/dist/redact.js +37 -0
  70. package/dist/redact.js.map +1 -0
  71. package/dist/runtime.d.ts +44 -0
  72. package/dist/runtime.js +94 -0
  73. package/dist/runtime.js.map +1 -0
  74. package/dist/scrub.d.ts +17 -0
  75. package/dist/scrub.js +124 -0
  76. package/dist/scrub.js.map +1 -0
  77. package/dist/secrets.d.ts +59 -0
  78. package/dist/secrets.js +220 -0
  79. package/dist/secrets.js.map +1 -0
  80. package/dist/ssn.d.ts +33 -0
  81. package/dist/ssn.js +170 -0
  82. package/dist/ssn.js.map +1 -0
  83. package/dist/state.d.ts +46 -0
  84. package/dist/state.js +23 -0
  85. package/dist/state.js.map +1 -0
  86. package/dist/sync.d.ts +60 -0
  87. package/dist/sync.js +190 -0
  88. package/dist/sync.js.map +1 -0
  89. package/examples/com.plaud-notes-to-notion.plist +34 -0
  90. package/examples/install-launchagent.sh +16 -0
  91. package/package.json +37 -0
@@ -0,0 +1,46 @@
1
+ export interface FileState {
2
+ /** baseline = finished before cutover (the Zap's); pending = waiting on Plaud or a retry. */
3
+ status: "pending" | "done" | "baseline";
4
+ /** Waiting longer than PENDING_FAST_HOURS: re-checked on deep scans only. */
5
+ slow?: boolean;
6
+ firstSeen: string;
7
+ name?: string;
8
+ recorded?: string;
9
+ /** Labels are computed once and reused on retries so Summary and Transcript always match. */
10
+ title?: string;
11
+ participants?: string[];
12
+ labelSource?: "llm" | "fallback";
13
+ llmAttempts: number;
14
+ summaryPageId?: string;
15
+ transcriptPageId?: string;
16
+ ssnRedactions?: number;
17
+ cardRedactions?: number;
18
+ lastError?: string;
19
+ doneAt?: string;
20
+ }
21
+ export interface ProfileState {
22
+ version: 1;
23
+ files: Record<string, FileState>;
24
+ lastSyncAt?: string;
25
+ lastFullSyncAt?: string;
26
+ /** When the one-time cutover snapshot ran. */
27
+ baselineAt?: string;
28
+ /**
29
+ * Summary page id → its Transcript sibling, as last seen live. Lets the sync
30
+ * notice a Summary that was moved to the trash and trash its Transcript too.
31
+ */
32
+ pairs?: Record<string, PairState>;
33
+ }
34
+ export interface PairState {
35
+ transcriptId: string;
36
+ minute: string;
37
+ /** Set once either side of the pair is in the trash; restoring either side restores both. */
38
+ trashedAt?: string;
39
+ /** Which side(s) the service trashed, or "both" when both were trashed together. */
40
+ trashed?: "summary" | "transcript" | "both";
41
+ }
42
+ export declare function emptyState(): ProfileState;
43
+ export declare function loadState(profile: string, env?: NodeJS.ProcessEnv): ProfileState;
44
+ export declare function saveState(profile: string, state: ProfileState, env?: NodeJS.ProcessEnv): void;
45
+ /** Forget finished files after a month; the startAfter watermark keeps them from coming back. */
46
+ export declare function pruneState(state: ProfileState, now: Date): void;
package/dist/state.js ADDED
@@ -0,0 +1,23 @@
1
+ import { STATE_KEEP_DAYS } from "./constants.js";
2
+ import { getProfileStatePath, readJson, writeJsonAtomic } from "./paths.js";
3
+ export function emptyState() {
4
+ return { version: 1, files: {} };
5
+ }
6
+ export function loadState(profile, env = process.env) {
7
+ const raw = readJson(getProfileStatePath(profile, env));
8
+ return raw && raw.version === 1 && raw.files ? raw : emptyState();
9
+ }
10
+ export function saveState(profile, state, env = process.env) {
11
+ writeJsonAtomic(getProfileStatePath(profile, env), state);
12
+ }
13
+ /** Forget finished files after a month; the startAfter watermark keeps them from coming back. */
14
+ export function pruneState(state, now) {
15
+ const cutoff = now.getTime() - STATE_KEEP_DAYS * 86_400_000;
16
+ for (const [id, f] of Object.entries(state.files)) {
17
+ const at = Date.parse(f.doneAt || f.firstSeen);
18
+ if (f.status !== "pending" && Number.isFinite(at) && at < cutoff) {
19
+ delete state.files[id];
20
+ }
21
+ }
22
+ }
23
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA8C5E,MAAM,UAAU,UAAU;IACxB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAe,EAAE,MAAyB,OAAO,CAAC,GAAG;IAC7E,MAAM,GAAG,GAAG,QAAQ,CAAe,mBAAmB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IACtE,OAAO,GAAG,IAAI,GAAG,CAAC,OAAO,KAAK,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAe,EAAE,KAAmB,EAAE,MAAyB,OAAO,CAAC,GAAG;IAClG,eAAe,CAAC,mBAAmB,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;AAC5D,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,UAAU,CAAC,KAAmB,EAAE,GAAS;IACvD,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,eAAe,GAAG,UAAU,CAAC;IAC5D,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAClD,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC;YACjE,OAAO,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;AACH,CAAC"}
package/dist/sync.d.ts ADDED
@@ -0,0 +1,60 @@
1
+ import type { NotionRow, NotionStore } from "./notion.js";
2
+ import type { ProfileState } from "./state.js";
3
+ /**
4
+ * Keeps each Transcript row's Meeting title and Participants equal to its
5
+ * Summary sibling (paired by Recorded, to the minute). Only Summary edits flow
6
+ * to Transcripts — never the other way. Ambiguous minutes (more than one
7
+ * Summary or Transcript) are skipped, as the retired Grok routine did.
8
+ */
9
+ export interface SyncDeps {
10
+ profile: string;
11
+ notion: NotionStore;
12
+ state: ProfileState;
13
+ save: () => void;
14
+ log: (msg: string) => void;
15
+ now?: () => Date;
16
+ dryRun?: boolean;
17
+ }
18
+ export interface SyncResult {
19
+ mode: "incremental" | "full";
20
+ groupsChecked: number;
21
+ updated: Array<{
22
+ id: string;
23
+ title: string;
24
+ changes: string[];
25
+ }>;
26
+ ambiguous: number;
27
+ }
28
+ export declare function planGroup(rows: NotionRow[]): {
29
+ transcript: NotionRow;
30
+ patch: {
31
+ title?: string;
32
+ participants?: string[];
33
+ };
34
+ changes: string[];
35
+ } | "ambiguous" | null;
36
+ export declare function runSync(deps: SyncDeps): Promise<SyncResult>;
37
+ export interface DeletionResult {
38
+ trashed: Array<{
39
+ pageId: string;
40
+ side: "summary" | "transcript";
41
+ minute: string;
42
+ }>;
43
+ restored: Array<{
44
+ pageId: string;
45
+ side: "summary" | "transcript";
46
+ minute: string;
47
+ }>;
48
+ tracked: number;
49
+ }
50
+ /**
51
+ * A Summary and its Transcript live and die together: trash either one and the
52
+ * other is trashed; restore either one from the trash and the other comes back.
53
+ *
54
+ * Notion queries never return trashed pages, so every cycle this remembers the
55
+ * live Summary↔Transcript pairs (same Recorded minute, exactly one of each) and
56
+ * checks any remembered page that stopped appearing. It only acts when Notion
57
+ * explicitly reports that page as in the trash — a 404 (permanent delete, or
58
+ * lost access) is never treated as a deletion.
59
+ */
60
+ export declare function runDeletionSync(deps: SyncDeps): Promise<DeletionResult>;
package/dist/sync.js ADDED
@@ -0,0 +1,190 @@
1
+ import { SYNC_FULL_EVERY_HOURS, SYNC_FULL_WINDOW_DAYS, TYPE_SUMMARY, TYPE_TRANSCRIPT } from "./constants.js";
2
+ const OVERLAP_MS = 2 * 60_000;
3
+ function minuteKey(recorded) {
4
+ if (!recorded) {
5
+ return null;
6
+ }
7
+ const t = Date.parse(recorded);
8
+ if (!Number.isFinite(t)) {
9
+ return null;
10
+ }
11
+ return new Date(Math.floor(t / 60_000) * 60_000).toISOString();
12
+ }
13
+ function sameSet(a, b) {
14
+ const sa = new Set(a);
15
+ return sa.size === new Set(b).size && b.every((x) => sa.has(x));
16
+ }
17
+ export function planGroup(rows) {
18
+ const summaries = rows.filter((r) => r.types.includes(TYPE_SUMMARY));
19
+ const transcripts = rows.filter((r) => r.types.includes(TYPE_TRANSCRIPT) && !r.types.includes(TYPE_SUMMARY));
20
+ if (summaries.length === 0 || transcripts.length === 0) {
21
+ return null;
22
+ }
23
+ if (summaries.length > 1 || transcripts.length > 1) {
24
+ return "ambiguous";
25
+ }
26
+ const [s] = summaries;
27
+ const [t] = transcripts;
28
+ const patch = {};
29
+ const changes = [];
30
+ if (s.title.trim() && s.title !== t.title) {
31
+ patch.title = s.title;
32
+ changes.push(`title "${t.title}" → "${s.title}"`);
33
+ }
34
+ if (!sameSet(s.participants, t.participants)) {
35
+ patch.participants = s.participants;
36
+ changes.push(`participants ${JSON.stringify(t.participants)} → ${JSON.stringify(s.participants)}`);
37
+ }
38
+ return changes.length ? { transcript: t, patch, changes } : null;
39
+ }
40
+ export async function runSync(deps) {
41
+ const now = (deps.now || (() => new Date()))();
42
+ const lastFull = deps.state.lastFullSyncAt ? Date.parse(deps.state.lastFullSyncAt) : 0;
43
+ const full = !deps.state.lastSyncAt || now.getTime() - lastFull > SYNC_FULL_EVERY_HOURS * 3_600_000;
44
+ const result = { mode: full ? "full" : "incremental", groupsChecked: 0, updated: [], ambiguous: 0 };
45
+ const groups = new Map();
46
+ if (full) {
47
+ const since = new Date(now.getTime() - SYNC_FULL_WINDOW_DAYS * 86_400_000).toISOString();
48
+ for (const row of await deps.notion.rowsRecordedSince(since)) {
49
+ const key = minuteKey(row.recorded);
50
+ if (key) {
51
+ groups.set(key, [...(groups.get(key) || []), row]);
52
+ }
53
+ }
54
+ }
55
+ else {
56
+ const since = new Date(Date.parse(deps.state.lastSyncAt) - OVERLAP_MS).toISOString();
57
+ for (const summary of await deps.notion.summariesEditedSince(since)) {
58
+ const key = minuteKey(summary.recorded);
59
+ if (key && !groups.has(key)) {
60
+ groups.set(key, await deps.notion.rowsAtMinute(key));
61
+ }
62
+ }
63
+ }
64
+ for (const [key, rows] of groups) {
65
+ result.groupsChecked++;
66
+ const plan = planGroup(rows);
67
+ if (plan === null) {
68
+ continue;
69
+ }
70
+ if (plan === "ambiguous") {
71
+ result.ambiguous++;
72
+ continue;
73
+ }
74
+ if (!deps.dryRun) {
75
+ await deps.notion.updateRow(plan.transcript.id, plan.patch);
76
+ }
77
+ result.updated.push({ id: plan.transcript.id, title: plan.patch.title ?? plan.transcript.title, changes: plan.changes });
78
+ deps.log(`[${deps.profile}] ${deps.dryRun ? "DRY RUN would sync" : "synced"} Transcript ${key}: ${plan.changes.join("; ")}`);
79
+ }
80
+ if (!deps.dryRun) {
81
+ deps.state.lastSyncAt = now.toISOString();
82
+ if (full) {
83
+ deps.state.lastFullSyncAt = now.toISOString();
84
+ }
85
+ deps.save();
86
+ }
87
+ return result;
88
+ }
89
+ /** How long a trashed pair stays eligible for restore (Notion keeps trash ~30 days). */
90
+ const RESTORE_WINDOW_MS = 30 * 86_400_000;
91
+ /**
92
+ * A Summary and its Transcript live and die together: trash either one and the
93
+ * other is trashed; restore either one from the trash and the other comes back.
94
+ *
95
+ * Notion queries never return trashed pages, so every cycle this remembers the
96
+ * live Summary↔Transcript pairs (same Recorded minute, exactly one of each) and
97
+ * checks any remembered page that stopped appearing. It only acts when Notion
98
+ * explicitly reports that page as in the trash — a 404 (permanent delete, or
99
+ * lost access) is never treated as a deletion.
100
+ */
101
+ export async function runDeletionSync(deps) {
102
+ const now = (deps.now || (() => new Date()))();
103
+ const pairs = (deps.state.pairs ||= {});
104
+ const result = { trashed: [], restored: [], tracked: 0 };
105
+ const since = new Date(now.getTime() - SYNC_FULL_WINDOW_DAYS * 86_400_000).toISOString();
106
+ const rows = await deps.notion.rowsRecordedSince(since);
107
+ const live = new Set(rows.map((r) => r.id));
108
+ const verb = (v) => (deps.dryRun ? `DRY RUN would ${v}` : v);
109
+ const setTrashed = async (pageId, side, minute, inTrash, why) => {
110
+ if (!deps.dryRun) {
111
+ await deps.notion.setTrashed(pageId, inTrash);
112
+ }
113
+ (inTrash ? result.trashed : result.restored).push({ pageId, side, minute });
114
+ deps.log(`[${deps.profile}] ${verb(inTrash ? "trash" : "restore")} ${side === "summary" ? "Summary" : "Transcript"} ${minute} (${why})`);
115
+ };
116
+ for (const [summaryId, pair] of Object.entries(pairs)) {
117
+ const summaryLive = live.has(summaryId);
118
+ const transcriptLive = live.has(pair.transcriptId);
119
+ if (pair.trashedAt) {
120
+ if (summaryLive || transcriptLive) {
121
+ // One side came back from the trash: bring the other back too.
122
+ if (!summaryLive) {
123
+ await setTrashed(summaryId, "summary", pair.minute, false, "its Transcript was restored");
124
+ }
125
+ if (!transcriptLive) {
126
+ await setTrashed(pair.transcriptId, "transcript", pair.minute, false, "its Summary was restored");
127
+ }
128
+ if (!deps.dryRun) {
129
+ delete pair.trashedAt;
130
+ delete pair.trashed;
131
+ }
132
+ }
133
+ else if (now.getTime() - Date.parse(pair.trashedAt) > RESTORE_WINDOW_MS) {
134
+ delete pairs[summaryId];
135
+ }
136
+ continue;
137
+ }
138
+ if (summaryLive && transcriptLive) {
139
+ continue;
140
+ }
141
+ const summaryState = summaryLive ? "live" : await deps.notion.pageState(summaryId);
142
+ const transcriptState = transcriptLive ? "live" : await deps.notion.pageState(pair.transcriptId);
143
+ if (summaryState === "trashed" && transcriptState === "live") {
144
+ await setTrashed(pair.transcriptId, "transcript", pair.minute, true, "its Summary was deleted");
145
+ if (!deps.dryRun) {
146
+ pair.trashedAt = now.toISOString();
147
+ pair.trashed = "transcript";
148
+ }
149
+ }
150
+ else if (transcriptState === "trashed" && summaryState === "live") {
151
+ await setTrashed(summaryId, "summary", pair.minute, true, "its Transcript was deleted");
152
+ if (!deps.dryRun) {
153
+ pair.trashedAt = now.toISOString();
154
+ pair.trashed = "summary";
155
+ }
156
+ }
157
+ else if (summaryState === "trashed" && transcriptState === "trashed") {
158
+ if (!deps.dryRun) {
159
+ pair.trashedAt = now.toISOString(); // both deleted by hand — still restore together
160
+ pair.trashed = "both";
161
+ }
162
+ }
163
+ else {
164
+ if (summaryState === "unknown" || transcriptState === "unknown") {
165
+ deps.log(`[${deps.profile}] a page for ${pair.minute} is no longer visible (not in trash); leaving its partner alone`);
166
+ }
167
+ delete pairs[summaryId]; // aged out of the window, moved (Recorded edited), or not visible — re-paired below if live
168
+ }
169
+ }
170
+ const groups = new Map();
171
+ for (const row of rows) {
172
+ const key = minuteKey(row.recorded);
173
+ if (key) {
174
+ groups.set(key, [...(groups.get(key) || []), row]);
175
+ }
176
+ }
177
+ for (const [minute, group] of groups) {
178
+ const summaries = group.filter((r) => r.types.includes(TYPE_SUMMARY));
179
+ const transcripts = group.filter((r) => r.types.includes(TYPE_TRANSCRIPT) && !r.types.includes(TYPE_SUMMARY));
180
+ if (summaries.length === 1 && transcripts.length === 1 && !pairs[summaries[0].id]?.trashedAt) {
181
+ pairs[summaries[0].id] = { transcriptId: transcripts[0].id, minute };
182
+ }
183
+ }
184
+ result.tracked = Object.values(pairs).filter((p) => !p.trashedAt).length;
185
+ if (!deps.dryRun) {
186
+ deps.save();
187
+ }
188
+ return result;
189
+ }
190
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string>com.plaud-notes-to-notion</string>
7
+ <!-- Always-on daemon: polls Plaud every pollSeconds and syncs every syncSeconds (config.json). -->
8
+ <key>ProgramArguments</key>
9
+ <array>
10
+ <string>REPLACE_NODE</string>
11
+ <string>REPLACE_BIN</string>
12
+ <string>run</string>
13
+ </array>
14
+ <key>RunAtLoad</key>
15
+ <true/>
16
+ <key>KeepAlive</key>
17
+ <true/>
18
+ <key>ThrottleInterval</key>
19
+ <integer>30</integer>
20
+ <key>ProcessType</key>
21
+ <string>Background</string>
22
+ <key>EnvironmentVariables</key>
23
+ <dict>
24
+ <key>PATH</key>
25
+ <string>/usr/bin:/bin:/usr/sbin:/sbin</string>
26
+ <key>HOME</key>
27
+ <string>REPLACE_HOME</string>
28
+ </dict>
29
+ <key>StandardOutPath</key>
30
+ <string>REPLACE_HOME/Library/Logs/plaud-notes-to-notion.launchd.out.log</string>
31
+ <key>StandardErrorPath</key>
32
+ <string>REPLACE_HOME/Library/Logs/plaud-notes-to-notion.launchd.err.log</string>
33
+ </dict>
34
+ </plist>
@@ -0,0 +1,16 @@
1
+ #!/bin/sh
2
+ # Installs/refreshes the LaunchAgent for the current user (GUI session, so Keychain works).
3
+ # Usage: sh install-launchagent.sh [/path/to/bin/plaud-notes-to-notion.js]
4
+ # Secrets stay in Keychain (service plaud-notes-to-notion); nothing secret goes in the plist.
5
+ set -eu
6
+ LABEL=com.plaud-notes-to-notion
7
+ NODE=$(command -v node)
8
+ BIN=${1:-$(npm root -g)/plaud-notes-to-notion/bin/plaud-notes-to-notion.js}
9
+ DIR=$(cd "$(dirname "$0")" && pwd)
10
+ PLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
11
+ test -f "$BIN" || { echo "Not found: $BIN" >&2; exit 1; }
12
+ mkdir -p "$HOME/Library/LaunchAgents"
13
+ sed -e "s#REPLACE_NODE#$NODE#" -e "s#REPLACE_BIN#$BIN#" -e "s#REPLACE_HOME#$HOME#g" "$DIR/$LABEL.plist" > "$PLIST"
14
+ launchctl bootout "gui/$(id -u)/$LABEL" 2>/dev/null || true
15
+ launchctl bootstrap "gui/$(id -u)" "$PLIST"
16
+ echo "Loaded $LABEL → $NODE $BIN run"
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "plaud-notes-to-notion",
3
+ "version": "1.0.0",
4
+ "description": "Mac Mini LaunchAgent: new Plaud recordings → Plaud Notes Notion database (Summary + Transcript rows), plus Summary→Transcript title/participant sync. Replaces the Zapier Plaud → Notion Zaps.",
5
+ "type": "module",
6
+ "main": "dist/cli.js",
7
+ "bin": {
8
+ "plaud-notes-to-notion": "./bin/plaud-notes-to-notion.js"
9
+ },
10
+ "author": "Peter Coates",
11
+ "license": "MIT",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/sfls1397/Plaud-Notes-to-Notion.git"
15
+ },
16
+ "homepage": "https://github.com/sfls1397/Plaud-Notes-to-Notion#readme",
17
+ "keywords": ["plaud", "notion", "launchagent", "transcripts"],
18
+ "engines": {
19
+ "node": ">=20.0.0"
20
+ },
21
+ "files": ["dist/", "bin/", "examples/", "README.md", "LICENSE", "CHANGELOG.md"],
22
+ "os": ["darwin", "linux"],
23
+ "scripts": {
24
+ "build": "tsc",
25
+ "prepare": "tsc",
26
+ "start": "node dist/cli.js",
27
+ "test": "vitest run",
28
+ "test:watch": "vitest",
29
+ "typecheck": "tsc --noEmit",
30
+ "audit": "npm audit --omit=dev"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^22.10.0",
34
+ "typescript": "^5.7.0",
35
+ "vitest": "^2.1.0"
36
+ }
37
+ }