@alta-foundation/plaud-extractor 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 (163) hide show
  1. package/.env.example +9 -0
  2. package/.github/workflows/ci.yml +33 -0
  3. package/.github/workflows/publish.yml +46 -0
  4. package/CLAUDE.md +53 -0
  5. package/README.md +318 -0
  6. package/dist/PlaudExtractor.d.ts +61 -0
  7. package/dist/PlaudExtractor.d.ts.map +1 -0
  8. package/dist/PlaudExtractor.js +236 -0
  9. package/dist/PlaudExtractor.js.map +1 -0
  10. package/dist/auth/browser-auth.d.ts +10 -0
  11. package/dist/auth/browser-auth.d.ts.map +1 -0
  12. package/dist/auth/browser-auth.js +220 -0
  13. package/dist/auth/browser-auth.js.map +1 -0
  14. package/dist/auth/token-store.d.ts +9 -0
  15. package/dist/auth/token-store.d.ts.map +1 -0
  16. package/dist/auth/token-store.js +74 -0
  17. package/dist/auth/token-store.js.map +1 -0
  18. package/dist/auth/types.d.ts +266 -0
  19. package/dist/auth/types.d.ts.map +1 -0
  20. package/dist/auth/types.js +32 -0
  21. package/dist/auth/types.js.map +1 -0
  22. package/dist/cli/bin.d.ts +3 -0
  23. package/dist/cli/bin.d.ts.map +1 -0
  24. package/dist/cli/bin.js +30 -0
  25. package/dist/cli/bin.js.map +1 -0
  26. package/dist/cli/commands/auth.d.ts +3 -0
  27. package/dist/cli/commands/auth.d.ts.map +1 -0
  28. package/dist/cli/commands/auth.js +22 -0
  29. package/dist/cli/commands/auth.js.map +1 -0
  30. package/dist/cli/commands/backfill.d.ts +3 -0
  31. package/dist/cli/commands/backfill.d.ts.map +1 -0
  32. package/dist/cli/commands/backfill.js +59 -0
  33. package/dist/cli/commands/backfill.js.map +1 -0
  34. package/dist/cli/commands/sync.d.ts +3 -0
  35. package/dist/cli/commands/sync.d.ts.map +1 -0
  36. package/dist/cli/commands/sync.js +55 -0
  37. package/dist/cli/commands/sync.js.map +1 -0
  38. package/dist/cli/commands/verify.d.ts +3 -0
  39. package/dist/cli/commands/verify.d.ts.map +1 -0
  40. package/dist/cli/commands/verify.js +28 -0
  41. package/dist/cli/commands/verify.js.map +1 -0
  42. package/dist/cli/exit-codes.d.ts +8 -0
  43. package/dist/cli/exit-codes.d.ts.map +1 -0
  44. package/dist/cli/exit-codes.js +16 -0
  45. package/dist/cli/exit-codes.js.map +1 -0
  46. package/dist/cli/options.d.ts +31 -0
  47. package/dist/cli/options.d.ts.map +1 -0
  48. package/dist/cli/options.js +11 -0
  49. package/dist/cli/options.js.map +1 -0
  50. package/dist/client/endpoints.d.ts +26 -0
  51. package/dist/client/endpoints.d.ts.map +1 -0
  52. package/dist/client/endpoints.js +54 -0
  53. package/dist/client/endpoints.js.map +1 -0
  54. package/dist/client/http.d.ts +17 -0
  55. package/dist/client/http.d.ts.map +1 -0
  56. package/dist/client/http.js +92 -0
  57. package/dist/client/http.js.map +1 -0
  58. package/dist/client/plaud-client.d.ts +14 -0
  59. package/dist/client/plaud-client.d.ts.map +1 -0
  60. package/dist/client/plaud-client.js +216 -0
  61. package/dist/client/plaud-client.js.map +1 -0
  62. package/dist/client/types.d.ts +154 -0
  63. package/dist/client/types.d.ts.map +1 -0
  64. package/dist/client/types.js +41 -0
  65. package/dist/client/types.js.map +1 -0
  66. package/dist/errors.d.ts +24 -0
  67. package/dist/errors.d.ts.map +1 -0
  68. package/dist/errors.js +51 -0
  69. package/dist/errors.js.map +1 -0
  70. package/dist/index.d.ts +7 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +5 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/logger.d.ts +9 -0
  75. package/dist/logger.d.ts.map +1 -0
  76. package/dist/logger.js +37 -0
  77. package/dist/logger.js.map +1 -0
  78. package/dist/mcp/job-tools.d.ts +3 -0
  79. package/dist/mcp/job-tools.d.ts.map +1 -0
  80. package/dist/mcp/job-tools.js +108 -0
  81. package/dist/mcp/job-tools.js.map +1 -0
  82. package/dist/mcp/read-tools.d.ts +3 -0
  83. package/dist/mcp/read-tools.d.ts.map +1 -0
  84. package/dist/mcp/read-tools.js +173 -0
  85. package/dist/mcp/read-tools.js.map +1 -0
  86. package/dist/mcp/server.d.ts +3 -0
  87. package/dist/mcp/server.d.ts.map +1 -0
  88. package/dist/mcp/server.js +32 -0
  89. package/dist/mcp/server.js.map +1 -0
  90. package/dist/storage/atomic.d.ts +5 -0
  91. package/dist/storage/atomic.d.ts.map +1 -0
  92. package/dist/storage/atomic.js +51 -0
  93. package/dist/storage/atomic.js.map +1 -0
  94. package/dist/storage/checksums.d.ts +15 -0
  95. package/dist/storage/checksums.d.ts.map +1 -0
  96. package/dist/storage/checksums.js +56 -0
  97. package/dist/storage/checksums.js.map +1 -0
  98. package/dist/storage/dataset-writer.d.ts +21 -0
  99. package/dist/storage/dataset-writer.d.ts.map +1 -0
  100. package/dist/storage/dataset-writer.js +52 -0
  101. package/dist/storage/dataset-writer.js.map +1 -0
  102. package/dist/storage/paths.d.ts +9 -0
  103. package/dist/storage/paths.d.ts.map +1 -0
  104. package/dist/storage/paths.js +38 -0
  105. package/dist/storage/paths.js.map +1 -0
  106. package/dist/storage/recording-store.d.ts +24 -0
  107. package/dist/storage/recording-store.d.ts.map +1 -0
  108. package/dist/storage/recording-store.js +161 -0
  109. package/dist/storage/recording-store.js.map +1 -0
  110. package/dist/sync/download-queue.d.ts +21 -0
  111. package/dist/sync/download-queue.d.ts.map +1 -0
  112. package/dist/sync/download-queue.js +82 -0
  113. package/dist/sync/download-queue.js.map +1 -0
  114. package/dist/sync/incremental.d.ts +21 -0
  115. package/dist/sync/incremental.d.ts.map +1 -0
  116. package/dist/sync/incremental.js +96 -0
  117. package/dist/sync/incremental.js.map +1 -0
  118. package/dist/sync/sync-engine.d.ts +6 -0
  119. package/dist/sync/sync-engine.d.ts.map +1 -0
  120. package/dist/sync/sync-engine.js +135 -0
  121. package/dist/sync/sync-engine.js.map +1 -0
  122. package/dist/sync/types.d.ts +130 -0
  123. package/dist/sync/types.d.ts.map +1 -0
  124. package/dist/sync/types.js +17 -0
  125. package/dist/sync/types.js.map +1 -0
  126. package/dist/transcript/formatter.d.ts +4 -0
  127. package/dist/transcript/formatter.d.ts.map +1 -0
  128. package/dist/transcript/formatter.js +88 -0
  129. package/dist/transcript/formatter.js.map +1 -0
  130. package/package.json +41 -0
  131. package/src/PlaudExtractor.ts +275 -0
  132. package/src/auth/browser-auth.ts +248 -0
  133. package/src/auth/token-store.ts +79 -0
  134. package/src/auth/types.ts +41 -0
  135. package/src/cli/bin.ts +30 -0
  136. package/src/cli/commands/auth.ts +27 -0
  137. package/src/cli/commands/backfill.ts +77 -0
  138. package/src/cli/commands/sync.ts +71 -0
  139. package/src/cli/commands/verify.ts +31 -0
  140. package/src/cli/exit-codes.ts +14 -0
  141. package/src/cli/options.ts +10 -0
  142. package/src/client/endpoints.ts +62 -0
  143. package/src/client/http.ts +110 -0
  144. package/src/client/plaud-client.ts +268 -0
  145. package/src/client/types.ts +62 -0
  146. package/src/errors.ts +57 -0
  147. package/src/index.ts +17 -0
  148. package/src/logger.ts +49 -0
  149. package/src/mcp/job-tools.ts +156 -0
  150. package/src/mcp/read-tools.ts +204 -0
  151. package/src/mcp/server.ts +39 -0
  152. package/src/storage/atomic.ts +51 -0
  153. package/src/storage/checksums.ts +76 -0
  154. package/src/storage/dataset-writer.ts +74 -0
  155. package/src/storage/paths.ts +44 -0
  156. package/src/storage/recording-store.ts +182 -0
  157. package/src/sync/download-queue.ts +102 -0
  158. package/src/sync/incremental.ts +111 -0
  159. package/src/sync/sync-engine.ts +183 -0
  160. package/src/sync/types.ts +64 -0
  161. package/src/transcript/formatter.ts +91 -0
  162. package/tsconfig.build.json +8 -0
  163. package/tsconfig.json +19 -0
@@ -0,0 +1,236 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import { createLogger, setLogger } from './logger.js';
5
+ import { loadCredentials, saveCredentials, isExpired } from './auth/token-store.js';
6
+ import { runBrowserAuth } from './auth/browser-auth.js';
7
+ import { PlaudApiClient } from './client/plaud-client.js';
8
+ import { SyncEngine } from './sync/sync-engine.js';
9
+ import { IncrementalTracker } from './sync/incremental.js';
10
+ import { verifyChecksums } from './storage/checksums.js';
11
+ import { recordingDir, defaultOutDir } from './storage/paths.js';
12
+ import { AuthError } from './errors.js';
13
+ export class PlaudExtractor {
14
+ outDir;
15
+ engine;
16
+ constructor(config = {}) {
17
+ this.outDir = config.outDir
18
+ ? path.resolve(config.outDir.replace(/^~/, os.homedir()))
19
+ : defaultOutDir();
20
+ if (config.logger) {
21
+ setLogger(config.logger);
22
+ }
23
+ else {
24
+ createLogger(this.outDir, { verbose: config.verbose, redact: config.redact });
25
+ }
26
+ this.engine = new SyncEngine();
27
+ }
28
+ /**
29
+ * Launch browser for authentication.
30
+ * Saves credentials to ~/.alta/plaud-auth.json.
31
+ */
32
+ async authenticate(opts = {}) {
33
+ const session = await runBrowserAuth(opts);
34
+ await saveCredentials(session);
35
+ }
36
+ /**
37
+ * Check if credentials exist and are not expired.
38
+ */
39
+ async isAuthenticated() {
40
+ const creds = await loadCredentials();
41
+ if (!creds)
42
+ return false;
43
+ if (isExpired(creds))
44
+ return false;
45
+ return true;
46
+ }
47
+ /**
48
+ * Incremental sync: only download new or changed recordings since last run.
49
+ * If the token expires mid-sync, re-authenticates automatically and retries once.
50
+ */
51
+ async sync(opts = {}) {
52
+ return this.runWithReauth(opts, 'sync');
53
+ }
54
+ /**
55
+ * Full backfill: re-evaluate all recordings regardless of sync state.
56
+ * If the token expires mid-backfill, re-authenticates automatically and retries once.
57
+ */
58
+ async backfill(opts = {}) {
59
+ return this.runWithReauth(opts, 'backfill');
60
+ }
61
+ /**
62
+ * Run sync/backfill, and if a token-expired AuthError occurs mid-run,
63
+ * automatically re-authenticate and retry once.
64
+ */
65
+ async runWithReauth(opts, mode) {
66
+ try {
67
+ const client = await this.buildClient();
68
+ return await this.engine.run(client, this.buildSyncOptions(opts), mode);
69
+ }
70
+ catch (err) {
71
+ if (!(err instanceof AuthError))
72
+ throw err;
73
+ // Token expired or rejected mid-run — re-authenticate and try once more
74
+ console.error('\nSession expired during sync. Re-authenticating...');
75
+ await this.authenticate();
76
+ console.log('Re-authenticated. Resuming sync...\n');
77
+ const client = await this.buildClient();
78
+ return this.engine.run(client, this.buildSyncOptions(opts), mode);
79
+ }
80
+ }
81
+ /**
82
+ * Walk all recording folders and verify checksums.
83
+ * With repair=true, re-download any file with a mismatch.
84
+ */
85
+ async verify(opts = {}) {
86
+ const client = opts.repair ? await this.buildClient() : null;
87
+ const tracker = new IncrementalTracker();
88
+ await tracker.load(this.outDir);
89
+ const result = { scanned: 0, ok: 0, failed: 0, repaired: 0, issues: [] };
90
+ const recordingIds = tracker.getAllRecordingIds();
91
+ for (const id of recordingIds) {
92
+ const state = tracker.getRecordingState(id);
93
+ if (!state)
94
+ continue;
95
+ const dir = recordingDir(this.outDir, state.recordedAt, id);
96
+ result.scanned++;
97
+ try {
98
+ const mismatches = await verifyChecksums(dir);
99
+ if (mismatches.length === 0) {
100
+ result.ok++;
101
+ tracker.markVerified(id);
102
+ }
103
+ else {
104
+ result.failed++;
105
+ for (const m of mismatches) {
106
+ result.issues.push({
107
+ recordingId: id,
108
+ file: path.basename(m.filePath),
109
+ issue: `checksum mismatch (expected: ${m.expected.slice(0, 8)}..., got: ${m.actual === 'MISSING' ? 'MISSING' : m.actual.slice(0, 8) + '...'})`,
110
+ });
111
+ }
112
+ // TODO: repair support requires re-fetching the recording object
113
+ // For now, log the mismatch
114
+ }
115
+ }
116
+ catch (err) {
117
+ result.failed++;
118
+ result.issues.push({ recordingId: id, file: '', issue: String(err) });
119
+ }
120
+ }
121
+ await tracker.persist(this.outDir);
122
+ return result;
123
+ }
124
+ /**
125
+ * Export all local recordings to a JSONL dataset file.
126
+ * Returns the path to the generated file.
127
+ */
128
+ async exportDataset(opts = {}) {
129
+ const { DatasetWriter } = await import('./storage/dataset-writer.js');
130
+ const { default: fsSync } = await import('node:fs');
131
+ // Walk recordings dir and collect existing transcript data
132
+ const datasetWriter = new DatasetWriter(this.outDir);
133
+ await datasetWriter.open();
134
+ // Re-generate from existing transcript.json files on disk
135
+ const recordingsBase = path.join(this.outDir, 'recordings');
136
+ try {
137
+ await this.walkAndExport(recordingsBase, datasetWriter);
138
+ }
139
+ finally {
140
+ await datasetWriter.close();
141
+ }
142
+ return datasetWriter.path;
143
+ }
144
+ async walkAndExport(recordingsBase, dataset) {
145
+ const { PlaudRecordingSchema } = await import('./client/types.js');
146
+ const { PlaudTranscriptSchema } = await import('./client/types.js');
147
+ // Walk year/month/dir structure
148
+ let yearDirs;
149
+ try {
150
+ yearDirs = await fs.readdir(recordingsBase);
151
+ }
152
+ catch {
153
+ return;
154
+ }
155
+ for (const year of yearDirs) {
156
+ const yearPath = path.join(recordingsBase, year);
157
+ let monthDirs;
158
+ try {
159
+ monthDirs = await fs.readdir(yearPath);
160
+ }
161
+ catch {
162
+ continue;
163
+ }
164
+ for (const month of monthDirs) {
165
+ const monthPath = path.join(yearPath, month);
166
+ let recDirs;
167
+ try {
168
+ recDirs = await fs.readdir(monthPath);
169
+ }
170
+ catch {
171
+ continue;
172
+ }
173
+ for (const recDir of recDirs) {
174
+ const recPath = path.join(monthPath, recDir);
175
+ try {
176
+ const metaRaw = await fs.readFile(path.join(recPath, 'meta.json'), 'utf8');
177
+ const transcriptRaw = await fs.readFile(path.join(recPath, 'transcript.json'), 'utf8');
178
+ const meta = JSON.parse(metaRaw);
179
+ const transcriptData = JSON.parse(transcriptRaw);
180
+ // Reconstruct minimal PlaudRecording from meta.json
181
+ const recording = PlaudRecordingSchema.parse({
182
+ id: meta['source_recording_id'],
183
+ title: meta['title'],
184
+ duration: meta['duration_seconds'],
185
+ recordedAt: meta['recorded_at'],
186
+ createdAt: meta['recorded_at'],
187
+ updatedAt: meta['recorded_at'],
188
+ hasTranscript: true,
189
+ _raw: meta,
190
+ });
191
+ const fullText = (transcriptData['segments'] ?? [])
192
+ .map(s => s.text ?? '')
193
+ .filter(Boolean)
194
+ .join('\n\n');
195
+ const transcript = PlaudTranscriptSchema.parse({
196
+ recordingId: String(meta['source_recording_id'] ?? ''),
197
+ duration: Number(meta['duration_seconds'] ?? 0),
198
+ segments: transcriptData['segments'] ?? [],
199
+ fullText,
200
+ _raw: transcriptData,
201
+ });
202
+ await dataset.append(this.outDir, recording, transcript);
203
+ }
204
+ catch {
205
+ // Skip recordings with missing/invalid files
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
211
+ async buildClient() {
212
+ const creds = await loadCredentials();
213
+ if (!creds) {
214
+ throw new AuthError("No credentials found — run 'alta-plaud auth' to authenticate");
215
+ }
216
+ if (isExpired(creds)) {
217
+ throw new AuthError("Credentials expired — run 'alta-plaud auth' to re-authenticate");
218
+ }
219
+ return new PlaudApiClient(creds);
220
+ }
221
+ buildSyncOptions(partial) {
222
+ return {
223
+ outDir: this.outDir,
224
+ since: partial.since,
225
+ limit: partial.limit,
226
+ concurrency: partial.concurrency ?? 3,
227
+ formats: partial.formats ?? ['json', 'txt', 'md'],
228
+ includeDataset: partial.includeDataset ?? true,
229
+ dryRun: partial.dryRun ?? false,
230
+ };
231
+ }
232
+ get dataDir() {
233
+ return this.outDir;
234
+ }
235
+ }
236
+ //# sourceMappingURL=PlaudExtractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PlaudExtractor.js","sourceRoot":"","sources":["../src/PlaudExtractor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { AuthSession } from './types.js';
2
+ export interface BrowserAuthOptions {
3
+ headless?: boolean;
4
+ email?: string;
5
+ password?: string;
6
+ /** How long to wait for the user to log in (ms). Default: 5 minutes. */
7
+ loginTimeoutMs?: number;
8
+ }
9
+ export declare function runBrowserAuth(opts?: BrowserAuthOptions): Promise<AuthSession>;
10
+ //# sourceMappingURL=browser-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-auth.d.ts","sourceRoot":"","sources":["../../src/auth/browser-auth.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAe,MAAM,YAAY,CAAA;AAI1D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,wBAAsB,cAAc,CAAC,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,WAAW,CAAC,CA4FxF"}
@@ -0,0 +1,220 @@
1
+ import { execSync } from 'node:child_process';
2
+ import { chromium } from 'playwright';
3
+ import { AuthError } from '../errors.js';
4
+ import { getLogger } from '../logger.js';
5
+ import { loadCredentials } from './token-store.js';
6
+ import { extractRegionalBaseUrl } from '../client/endpoints.js';
7
+ const PLAUD_APP_URL = 'https://web.plaud.ai';
8
+ export async function runBrowserAuth(opts = {}) {
9
+ const log = getLogger();
10
+ const launchOpts = {
11
+ channel: 'chrome',
12
+ headless: opts.headless ?? false,
13
+ args: ['--disable-blink-features=AutomationControlled'],
14
+ };
15
+ const browser = await chromium.launch(launchOpts).catch(async (err) => {
16
+ const msg = String(err);
17
+ if (msg.includes("Executable doesn't exist") || msg.includes('not found')) {
18
+ log.warn('System Chrome not found, falling back to Playwright Chromium (Google OAuth may be blocked)');
19
+ return chromium.launch({ headless: opts.headless ?? false }).catch(err2 => {
20
+ if (String(err2).includes("Executable doesn't exist")) {
21
+ log.info('Installing Playwright Chromium (one-time setup)...');
22
+ execSync('npx playwright install chromium', { stdio: 'inherit' });
23
+ return chromium.launch({ headless: opts.headless ?? false });
24
+ }
25
+ throw err2;
26
+ });
27
+ }
28
+ throw err;
29
+ });
30
+ const context = await browser.newContext({ userAgent: undefined });
31
+ const page = await context.newPage();
32
+ // Remove webdriver property that Google checks for automation detection
33
+ await page.addInitScript('Object.defineProperty(navigator, "webdriver", { get: () => undefined })');
34
+ // Inject existing plaud.ai cookies so we don't need a fresh login if session is still valid
35
+ await injectExistingCookies(context);
36
+ try {
37
+ log.info('Opening Plaud...');
38
+ // Set up Bearer token capture BEFORE navigation — the SPA fires API calls on load
39
+ const loginTimeoutMs = opts.loginTimeoutMs ?? 5 * 60_000;
40
+ const bearerTokenCapture = captureBearerToken(page, loginTimeoutMs, log);
41
+ await page.goto(PLAUD_APP_URL, { waitUntil: 'domcontentloaded' });
42
+ // Give SPA time to initialize and run its auth check (may redirect to /login)
43
+ await page.waitForLoadState('networkidle', { timeout: 10_000 }).catch(() => { });
44
+ if (opts.email && opts.password) {
45
+ await automatedLogin(page, opts.email, opts.password);
46
+ }
47
+ else if (isLoginUrl(page.url())) {
48
+ // Not logged in — prompt user and wait
49
+ console.log('\n──────────────────────────────────────────────────────────');
50
+ console.log(' Log in to Plaud in the browser window.');
51
+ console.log(' The browser will close automatically once connected.');
52
+ console.log(` (Waiting up to ${Math.round(loginTimeoutMs / 60_000)} minutes)`);
53
+ console.log('──────────────────────────────────────────────────────────\n');
54
+ }
55
+ else {
56
+ log.info('Already connected — capturing token...');
57
+ }
58
+ // Wait for Bearer token from any API request (fires on page load if session is active,
59
+ // or after login if the user needed to authenticate)
60
+ const authToken = await bearerTokenCapture;
61
+ log.info('Bearer token captured — closing browser');
62
+ const cookies = await context.cookies();
63
+ // Close browser without blocking — Chrome can take a long time to flush its profile
64
+ void browser.close().catch(() => { });
65
+ // Discover the correct regional API base URL (e.g. api-euc1.plaud.ai for EU users)
66
+ const apiBaseUrl = await discoverApiRegion(authToken);
67
+ log.info({ apiBaseUrl }, 'Regional API base URL discovered');
68
+ return {
69
+ cookies: cookies.map(c => ({
70
+ name: c.name,
71
+ value: c.value,
72
+ domain: c.domain,
73
+ path: c.path,
74
+ httpOnly: c.httpOnly,
75
+ secure: c.secure,
76
+ sameSite: c.sameSite,
77
+ expires: c.expires && c.expires > 0 ? c.expires : undefined,
78
+ })),
79
+ authToken,
80
+ apiBaseUrl,
81
+ capturedAt: new Date().toISOString(),
82
+ endpointMap: buildEndpointMap(apiBaseUrl),
83
+ };
84
+ }
85
+ catch (err) {
86
+ await browser.close().catch(() => { });
87
+ throw err;
88
+ }
89
+ }
90
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
91
+ /**
92
+ * Inject plaud.ai cookies from the previous auth session so the browser picks up
93
+ * an existing session without requiring the user to log in again.
94
+ */
95
+ async function injectExistingCookies(context) {
96
+ const log = getLogger();
97
+ const existing = await loadCredentials().catch(() => null);
98
+ if (!existing?.cookies?.length)
99
+ return;
100
+ const plaudCookies = existing.cookies.filter(c => c.domain === 'web.plaud.ai' || c.domain.endsWith('.plaud.ai') || c.domain === 'plaud.ai');
101
+ if (plaudCookies.length === 0)
102
+ return;
103
+ try {
104
+ await context.addCookies(plaudCookies.map(c => ({
105
+ name: c.name,
106
+ value: c.value,
107
+ domain: c.domain,
108
+ path: c.path,
109
+ httpOnly: c.httpOnly,
110
+ secure: c.secure,
111
+ sameSite: (c.sameSite ?? 'Lax'),
112
+ expires: c.expires ?? -1,
113
+ })));
114
+ log.debug({ count: plaudCookies.length }, 'Injected existing session cookies');
115
+ }
116
+ catch (err) {
117
+ log.debug({ err }, 'Could not inject existing cookies — fresh login required');
118
+ }
119
+ }
120
+ /**
121
+ * Wait for the first API request that carries a Bearer token.
122
+ * This fires automatically when:
123
+ * - The page loads with an existing authenticated session (cookies restored)
124
+ * - The user completes login via Google OAuth or email
125
+ *
126
+ * Resolves with the raw token string (without "bearer " prefix).
127
+ */
128
+ function captureBearerToken(page, timeoutMs, log) {
129
+ return new Promise((resolve, reject) => {
130
+ const timer = setTimeout(() => {
131
+ page.off('request', handler);
132
+ reject(new AuthError(`Login timeout after ${Math.round(timeoutMs / 60_000)} minutes — no token captured`));
133
+ }, timeoutMs);
134
+ const handler = (req) => {
135
+ const auth = req.headers()['authorization'] ?? req.headers()['Authorization'];
136
+ if (!auth)
137
+ return;
138
+ const token = auth.replace(/^bearer\s+/i, '').trim();
139
+ // Basic sanity check: JWT has 3 parts separated by dots
140
+ if (token.split('.').length === 3) {
141
+ clearTimeout(timer);
142
+ page.off('request', handler);
143
+ log.debug({ url: req.url() }, 'Bearer token found in request');
144
+ resolve(token);
145
+ }
146
+ };
147
+ page.on('request', handler);
148
+ });
149
+ }
150
+ /** Discover the correct regional API base URL (e.g. https://api-euc1.plaud.ai). */
151
+ async function discoverApiRegion(token) {
152
+ const log = getLogger();
153
+ try {
154
+ // The global endpoint returns a region-redirect response pointing to the right server
155
+ const res = await fetch('https://api.plaud.ai/user/me', {
156
+ headers: {
157
+ 'Authorization': `bearer ${token}`,
158
+ 'app-platform': 'web',
159
+ 'Origin': 'https://web.plaud.ai',
160
+ },
161
+ });
162
+ const body = await res.json();
163
+ const regional = extractRegionalBaseUrl(body);
164
+ if (regional)
165
+ return regional;
166
+ // If the global endpoint returns user data directly (no redirect), it IS the right base
167
+ if (body?.data_user)
168
+ return 'https://api.plaud.ai';
169
+ }
170
+ catch (err) {
171
+ log.debug({ err }, 'Region discovery failed — using global API');
172
+ }
173
+ return 'https://api.plaud.ai';
174
+ }
175
+ /** Build the complete endpoint map from the known regional API base URL. */
176
+ function buildEndpointMap(apiBaseUrl) {
177
+ return {
178
+ listRecordings: `${apiBaseUrl}/file/simple/web`,
179
+ batchDetail: `${apiBaseUrl}/file/list`,
180
+ getAudioUrl: `${apiBaseUrl}/file/temp-url`,
181
+ userProfile: `${apiBaseUrl}/user/me`,
182
+ apiBaseUrl,
183
+ };
184
+ }
185
+ function isLoginUrl(url) {
186
+ try {
187
+ const p = new URL(url).pathname;
188
+ return p.startsWith('/login') || p.startsWith('/signin') || p.startsWith('/auth');
189
+ }
190
+ catch {
191
+ return false;
192
+ }
193
+ }
194
+ async function automatedLogin(page, email, password) {
195
+ const log = getLogger();
196
+ log.info('Attempting automated login...');
197
+ const emailSelectors = [
198
+ 'input[type="email"]', 'input[name="email"]',
199
+ 'input[name="username"]', '[data-testid="email"]', '#email',
200
+ ];
201
+ const passwordSelectors = [
202
+ 'input[type="password"]', 'input[name="password"]',
203
+ '[data-testid="password"]', '#password',
204
+ ];
205
+ for (const sel of emailSelectors) {
206
+ if (await page.locator(sel).count() > 0) {
207
+ await page.fill(sel, email);
208
+ break;
209
+ }
210
+ }
211
+ for (const sel of passwordSelectors) {
212
+ if (await page.locator(sel).count() > 0) {
213
+ await page.fill(sel, password);
214
+ break;
215
+ }
216
+ }
217
+ await page.click('button[type="submit"], [type="submit"], button:has-text("Login"), button:has-text("Sign in")');
218
+ await page.waitForLoadState('networkidle', { timeout: 15_000 }).catch(() => undefined);
219
+ }
220
+ //# sourceMappingURL=browser-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-auth.js","sourceRoot":"","sources":["../../src/auth/browser-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAA6D,MAAM,YAAY,CAAA;AAChG,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAG/D,MAAM,aAAa,GAAG,sBAAsB,CAAA;AAU5C,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAA2B,EAAE;IAChE,MAAM,GAAG,GAAG,SAAS,EAAE,CAAA;IACvB,MAAM,UAAU,GAAG;QACjB,OAAO,EAAE,QAAiB;QAC1B,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK;QAChC,IAAI,EAAE,CAAC,+CAA+C,CAAC;KACxD,CAAA;IAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,KAAK,EAAC,GAAG,EAAC,EAAE;QAClE,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;QACvB,IAAI,GAAG,CAAC,QAAQ,CAAC,0BAA0B,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAC1E,GAAG,CAAC,IAAI,CAAC,4FAA4F,CAAC,CAAA;YACtG,OAAO,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;gBACxE,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,0BAA0B,CAAC,EAAE,CAAC;oBACtD,GAAG,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAA;oBAC9D,QAAQ,CAAC,iCAAiC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAA;oBACjE,OAAO,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK,EAAE,CAAC,CAAA;gBAC9D,CAAC;gBACD,MAAM,IAAI,CAAA;YACZ,CAAC,CAAC,CAAA;QACJ,CAAC;QACD,MAAM,GAAG,CAAA;IACX,CAAC,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAA;IAClE,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;IAEpC,wEAAwE;IACxE,MAAM,IAAI,CAAC,aAAa,CACtB,yEAAyE,CAC1E,CAAA;IAED,4FAA4F;IAC5F,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAA;IAEpC,IAAI,CAAC;QACH,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAA;QAE5B,kFAAkF;QAClF,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,IAAI,CAAC,GAAG,MAAM,CAAA;QACxD,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,CAAC,CAAA;QAExE,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,CAAA;QACjE,8EAA8E;QAC9E,MAAM,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QAE/E,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChC,MAAM,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QACvD,CAAC;aAAM,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;YAClC,uCAAuC;YACvC,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAA;YAC3E,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAA;YACvD,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAA;YACrE,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,MAAM,CAAC,WAAW,CAAC,CAAA;YAC/E,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAA;QAC7E,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAA;QACpD,CAAC;QAED,uFAAuF;QACvF,qDAAqD;QACrD,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAA;QAC1C,GAAG,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAA;QAEnD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;QACvC,oFAAoF;QACpF,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QAEpC,mFAAmF;QACnF,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,SAAS,CAAC,CAAA;QACrD,GAAG,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,EAAE,kCAAkC,CAAC,CAAA;QAE5D,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBACzB,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,QAAQ,EAAE,CAAC,CAAC,QAAiD;gBAC7D,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;aAC5D,CAAC,CAAC;YACH,SAAS;YACT,UAAU;YACV,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACpC,WAAW,EAAE,gBAAgB,CAAC,UAAU,CAAC;SAC1C,CAAA;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QACrC,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC;AAED,iFAAiF;AAEjF;;;GAGG;AACH,KAAK,UAAU,qBAAqB,CAAC,OAAuB;IAC1D,MAAM,GAAG,GAAG,SAAS,EAAE,CAAA;IACvB,MAAM,QAAQ,GAAG,MAAM,eAAe,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;IAC1D,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM;QAAE,OAAM;IAEtC,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAC1C,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,cAAc,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,UAAU,CAC9F,CAAA;IACD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAM;IAErC,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,UAAU,CACtB,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACrB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,KAAK,CAA8B;YAC5D,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC;SACzB,CAAC,CAAC,CACJ,CAAA;QACD,GAAG,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE,EAAE,mCAAmC,CAAC,CAAA;IAChF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,0DAA0D,CAAC,CAAA;IAChF,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,kBAAkB,CAAC,IAAU,EAAE,SAAiB,EAAE,GAAiC;IAC1F,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;YAC5B,MAAM,CAAC,IAAI,SAAS,CAAC,uBAAuB,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC,8BAA8B,CAAC,CAAC,CAAA;QAC5G,CAAC,EAAE,SAAS,CAAC,CAAA;QAEb,MAAM,OAAO,GAAG,CAAC,GAAc,EAAE,EAAE;YACjC,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,eAAe,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC,eAAe,CAAC,CAAA;YAC7E,IAAI,CAAC,IAAI;gBAAE,OAAM;YACjB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;YACpD,wDAAwD;YACxD,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAClC,YAAY,CAAC,KAAK,CAAC,CAAA;gBACnB,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;gBAC5B,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,EAAE,EAAE,+BAA+B,CAAC,CAAA;gBAC9D,OAAO,CAAC,KAAK,CAAC,CAAA;YAChB,CAAC;QACH,CAAC,CAAA;QAED,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,mFAAmF;AACnF,KAAK,UAAU,iBAAiB,CAAC,KAAa;IAC5C,MAAM,GAAG,GAAG,SAAS,EAAE,CAAA;IACvB,IAAI,CAAC;QACH,sFAAsF;QACtF,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,8BAA8B,EAAE;YACtD,OAAO,EAAE;gBACP,eAAe,EAAE,UAAU,KAAK,EAAE;gBAClC,cAAc,EAAE,KAAK;gBACrB,QAAQ,EAAE,sBAAsB;aACjC;SACF,CAAC,CAAA;QACF,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;QAC7B,MAAM,QAAQ,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAA;QAC7C,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAA;QAE7B,wFAAwF;QACxF,IAAK,IAAgC,EAAE,SAAS;YAAE,OAAO,sBAAsB,CAAA;IACjF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,4CAA4C,CAAC,CAAA;IAClE,CAAC;IACD,OAAO,sBAAsB,CAAA;AAC/B,CAAC;AAED,4EAA4E;AAC5E,SAAS,gBAAgB,CAAC,UAAkB;IAC1C,OAAO;QACL,cAAc,EAAE,GAAG,UAAU,kBAAkB;QAC/C,WAAW,EAAE,GAAG,UAAU,YAAY;QACtC,WAAW,EAAE,GAAG,UAAU,gBAAgB;QAC1C,WAAW,EAAE,GAAG,UAAU,UAAU;QACpC,UAAU;KACX,CAAA;AACH,CAAC;AAED,SAAS,UAAU,CAAC,GAAW;IAC7B,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAA;QAC/B,OAAO,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;IACnF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAU,EAAE,KAAa,EAAE,QAAgB;IACvE,MAAM,GAAG,GAAG,SAAS,EAAE,CAAA;IACvB,GAAG,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAA;IAEzC,MAAM,cAAc,GAAG;QACrB,qBAAqB,EAAE,qBAAqB;QAC5C,wBAAwB,EAAE,uBAAuB,EAAE,QAAQ;KAC5D,CAAA;IACD,MAAM,iBAAiB,GAAG;QACxB,wBAAwB,EAAE,wBAAwB;QAClD,0BAA0B,EAAE,WAAW;KACxC,CAAA;IAED,KAAK,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;QACjC,IAAI,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;YAAC,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAAC,MAAK;QAAC,CAAC;IACjF,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,iBAAiB,EAAE,CAAC;QACpC,IAAI,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;YAAC,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YAAC,MAAK;QAAC,CAAC;IACpF,CAAC;IAED,MAAM,IAAI,CAAC,KAAK,CACd,8FAA8F,CAC/F,CAAA;IACD,MAAM,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;AACxF,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { authTokenPath } from '../storage/paths.js';
2
+ import { type AuthSession, type StoredCredentials } from './types.js';
3
+ export declare function loadCredentials(): Promise<StoredCredentials | null>;
4
+ export declare function saveCredentials(session: AuthSession): Promise<void>;
5
+ /** Returns true if the stored credentials are expired. */
6
+ export declare function isExpired(creds: StoredCredentials): boolean;
7
+ export declare function cookieHeader(creds: StoredCredentials): string;
8
+ export { authTokenPath };
9
+ //# sourceMappingURL=token-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAA2B,KAAK,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAI9F,wBAAsB,eAAe,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAgBzE;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAKzE;AAED,0DAA0D;AAC1D,wBAAgB,SAAS,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CA2B3D;AAeD,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAE7D;AAED,OAAO,EAAE,aAAa,EAAE,CAAA"}
@@ -0,0 +1,74 @@
1
+ import fs from 'node:fs/promises';
2
+ import { authTokenPath } from '../storage/paths.js';
3
+ import { StoredCredentialsSchema } from './types.js';
4
+ import { writeFileAtomic } from '../storage/atomic.js';
5
+ import { getLogger } from '../logger.js';
6
+ export async function loadCredentials() {
7
+ const tokenPath = authTokenPath();
8
+ try {
9
+ const raw = await fs.readFile(tokenPath, 'utf8');
10
+ const json = JSON.parse(raw);
11
+ const result = StoredCredentialsSchema.safeParse(json);
12
+ if (!result.success) {
13
+ getLogger().warn({ issues: result.error.issues }, 'Stored credentials failed schema validation — re-authenticate');
14
+ return null;
15
+ }
16
+ return result.data;
17
+ }
18
+ catch (err) {
19
+ if (err.code === 'ENOENT')
20
+ return null;
21
+ getLogger().warn({ err }, 'Failed to read credentials file');
22
+ return null;
23
+ }
24
+ }
25
+ export async function saveCredentials(session) {
26
+ const tokenPath = authTokenPath();
27
+ const stored = { ...session, schemaVersion: 1 };
28
+ await writeFileAtomic(tokenPath, JSON.stringify(stored, null, 2));
29
+ getLogger().info({ path: tokenPath }, 'Auth credentials saved');
30
+ }
31
+ /** Returns true if the stored credentials are expired. */
32
+ export function isExpired(creds) {
33
+ const now = Date.now();
34
+ // Explicit expiresAt takes precedence
35
+ if (creds.expiresAt) {
36
+ return now > new Date(creds.expiresAt).getTime();
37
+ }
38
+ // If we have a JWT bearer token, decode the exp claim (most reliable)
39
+ if (creds.authToken) {
40
+ const jwtExp = decodeJwtExp(creds.authToken);
41
+ if (jwtExp !== null)
42
+ return now > jwtExp * 1000;
43
+ }
44
+ // Fallback: check only plaud.ai session cookies (ignore analytics/CDN cookies
45
+ // which have short TTLs and would cause false "expired" readings)
46
+ const plaudCookies = creds.cookies.filter(c => c.expires && c.expires > 0 && (c.domain.endsWith('.plaud.ai') || c.domain === 'plaud.ai'));
47
+ if (plaudCookies.length > 0) {
48
+ const minExpiry = Math.min(...plaudCookies.map(c => (c.expires ?? 0) * 1000));
49
+ if (minExpiry > 0 && now > minExpiry)
50
+ return true;
51
+ }
52
+ // Last resort: treat as expired after 30 days
53
+ const capturedAt = new Date(creds.capturedAt).getTime();
54
+ return now - capturedAt > 30 * 24 * 60 * 60 * 1000;
55
+ }
56
+ /** Decode the `exp` claim from a JWT (no signature verification — just decode). */
57
+ function decodeJwtExp(token) {
58
+ try {
59
+ const parts = token.split('.');
60
+ if (parts.length !== 3)
61
+ return null;
62
+ const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
63
+ const exp = payload['exp'];
64
+ return typeof exp === 'number' ? exp : null;
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ }
70
+ export function cookieHeader(creds) {
71
+ return creds.cookies.map(c => `${c.name}=${c.value}`).join('; ');
72
+ }
73
+ export { authTokenPath };
74
+ //# sourceMappingURL=token-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAA;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,uBAAuB,EAA4C,MAAM,YAAY,CAAA;AAC9F,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAExC,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,SAAS,GAAG,aAAa,EAAE,CAAA;IACjC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;QAChD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC5B,MAAM,MAAM,GAAG,uBAAuB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QACtD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,+DAA+D,CAAC,CAAA;YAClH,OAAO,IAAI,CAAA;QACb,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAA;IACpB,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;QACjE,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,iCAAiC,CAAC,CAAA;QAC5D,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAoB;IACxD,MAAM,SAAS,GAAG,aAAa,EAAE,CAAA;IACjC,MAAM,MAAM,GAAsB,EAAE,GAAG,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,CAAA;IAClE,MAAM,eAAe,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;IACjE,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,wBAAwB,CAAC,CAAA;AACjE,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,SAAS,CAAC,KAAwB;IAChD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IAEtB,sCAAsC;IACtC,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpB,OAAO,GAAG,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAA;IAClD,CAAC;IAED,sEAAsE;IACtE,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;QAC5C,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,GAAG,GAAG,MAAM,GAAG,IAAI,CAAA;IACjD,CAAC;IAED,8EAA8E;IAC9E,kEAAkE;IAClE,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CACvC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,CAC/F,CAAA;IACD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAA;QAC7E,IAAI,SAAS,GAAG,CAAC,IAAI,GAAG,GAAG,SAAS;YAAE,OAAO,IAAI,CAAA;IACnD,CAAC;IAED,8CAA8C;IAC9C,MAAM,UAAU,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAA;IACvD,OAAO,GAAG,GAAG,UAAU,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AACpD,CAAC;AAED,mFAAmF;AACnF,SAAS,YAAY,CAAC,KAAa;IACjC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC9B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAA;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAA4B,CAAA;QAC3G,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;QAC1B,OAAO,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAwB;IACnD,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAClE,CAAC;AAED,OAAO,EAAE,aAAa,EAAE,CAAA"}