@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,51 @@
1
+ import fs from 'node:fs/promises';
2
+ import { createWriteStream } from 'node:fs';
3
+ import path from 'node:path';
4
+ import crypto from 'node:crypto';
5
+ import { StorageError } from '../errors.js';
6
+ /** Write a file atomically: write to .tmp-<rand>, then rename. */
7
+ export async function writeFileAtomic(filePath, data) {
8
+ const tmpPath = `${filePath}.tmp-${crypto.randomBytes(4).toString('hex')}`;
9
+ try {
10
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
11
+ await fs.writeFile(tmpPath, data, { encoding: typeof data === 'string' ? 'utf8' : undefined });
12
+ await fs.rename(tmpPath, filePath);
13
+ }
14
+ catch (err) {
15
+ // Clean up tmp file on failure
16
+ await fs.unlink(tmpPath).catch(() => undefined);
17
+ throw new StorageError(`Failed to write ${filePath}`, filePath, err);
18
+ }
19
+ }
20
+ /** Stream a ReadableStream to a file atomically. */
21
+ export async function writeStreamAtomic(filePath, stream) {
22
+ const tmpPath = `${filePath}.tmp-${crypto.randomBytes(4).toString('hex')}`;
23
+ try {
24
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
25
+ await new Promise((resolve, reject) => {
26
+ const out = createWriteStream(tmpPath);
27
+ out.on('finish', resolve);
28
+ out.on('error', reject);
29
+ if (Symbol.asyncIterator in stream) {
30
+ ;
31
+ (async () => {
32
+ for await (const chunk of stream) {
33
+ out.write(chunk);
34
+ }
35
+ out.end();
36
+ })().catch(reject);
37
+ }
38
+ else {
39
+ ;
40
+ stream.pipe(out);
41
+ stream.on('error', reject);
42
+ }
43
+ });
44
+ await fs.rename(tmpPath, filePath);
45
+ }
46
+ catch (err) {
47
+ await fs.unlink(tmpPath).catch(() => undefined);
48
+ throw new StorageError(`Failed to write stream to ${filePath}`, filePath, err);
49
+ }
50
+ }
51
+ //# sourceMappingURL=atomic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic.js","sourceRoot":"","sources":["../../src/storage/atomic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { ChecksumMismatchError } from '../errors.js';
2
+ export interface FileChecksum {
3
+ sha256: string;
4
+ sizeBytes: number;
5
+ }
6
+ export interface ChecksumManifest {
7
+ schemaVersion: 1;
8
+ recordingId: string;
9
+ computedAt: string;
10
+ files: Record<string, FileChecksum>;
11
+ }
12
+ export declare function sha256File(filePath: string): Promise<string>;
13
+ export declare function writeChecksumManifest(dir: string, recordingId: string): Promise<ChecksumManifest>;
14
+ export declare function verifyChecksums(dir: string): Promise<ChecksumMismatchError[]>;
15
+ //# sourceMappingURL=checksums.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checksums.d.ts","sourceRoot":"","sources":["../../src/storage/checksums.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAEpD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,CAAC,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;CACpC;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGlE;AAED,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,gBAAgB,CAAC,CAqB3B;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC,CA0BnF"}
@@ -0,0 +1,56 @@
1
+ import crypto from 'node:crypto';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { writeFileAtomic } from './atomic.js';
5
+ import { ChecksumMismatchError } from '../errors.js';
6
+ export async function sha256File(filePath) {
7
+ const data = await fs.readFile(filePath);
8
+ return crypto.createHash('sha256').update(data).digest('hex');
9
+ }
10
+ export async function writeChecksumManifest(dir, recordingId) {
11
+ const files = await fs.readdir(dir);
12
+ const manifest = {
13
+ schemaVersion: 1,
14
+ recordingId,
15
+ computedAt: new Date().toISOString(),
16
+ files: {},
17
+ };
18
+ for (const file of files.filter(f => f !== 'checksums.json')) {
19
+ const fPath = path.join(dir, file);
20
+ const stat = await fs.stat(fPath);
21
+ if (!stat.isFile())
22
+ continue;
23
+ manifest.files[file] = {
24
+ sha256: await sha256File(fPath),
25
+ sizeBytes: stat.size,
26
+ };
27
+ }
28
+ await writeFileAtomic(path.join(dir, 'checksums.json'), JSON.stringify(manifest, null, 2));
29
+ return manifest;
30
+ }
31
+ export async function verifyChecksums(dir) {
32
+ const manifestPath = path.join(dir, 'checksums.json');
33
+ let manifest;
34
+ try {
35
+ const raw = await fs.readFile(manifestPath, 'utf8');
36
+ manifest = JSON.parse(raw);
37
+ }
38
+ catch {
39
+ return []; // No manifest yet — skip verification
40
+ }
41
+ const mismatches = [];
42
+ for (const [file, expected] of Object.entries(manifest.files)) {
43
+ const fPath = path.join(dir, file);
44
+ try {
45
+ const actual = await sha256File(fPath);
46
+ if (actual !== expected.sha256) {
47
+ mismatches.push(new ChecksumMismatchError(fPath, expected.sha256, actual));
48
+ }
49
+ }
50
+ catch {
51
+ mismatches.push(new ChecksumMismatchError(fPath, expected.sha256, 'MISSING'));
52
+ }
53
+ }
54
+ return mismatches;
55
+ }
56
+ //# sourceMappingURL=checksums.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checksums.js","sourceRoot":"","sources":["../../src/storage/checksums.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { PlaudRecording, PlaudTranscript } from '../client/types.js';
2
+ export interface DatasetEntry {
3
+ id: string;
4
+ title: string | null;
5
+ recorded_at: string;
6
+ duration_seconds: number;
7
+ language: string | null;
8
+ text: string;
9
+ path: string;
10
+ segment_count: number;
11
+ }
12
+ export declare class DatasetWriter {
13
+ private readonly filePath;
14
+ private stream;
15
+ constructor(outDir: string);
16
+ open(): Promise<void>;
17
+ append(outDir: string, recording: PlaudRecording, transcript: PlaudTranscript): Promise<void>;
18
+ close(): Promise<void>;
19
+ get path(): string;
20
+ }
21
+ //# sourceMappingURL=dataset-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset-writer.d.ts","sourceRoot":"","sources":["../../src/storage/dataset-writer.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAGzE,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,EAAE,MAAM,CAAA;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,MAAM,CAA2B;gBAE7B,MAAM,EAAE,MAAM;IAIpB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IASrB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IA0B7F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -0,0 +1,52 @@
1
+ import fs from 'node:fs/promises';
2
+ import { createWriteStream } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { datasetPath, recordingDir } from './paths.js';
5
+ import { StorageError } from '../errors.js';
6
+ export class DatasetWriter {
7
+ filePath;
8
+ stream = null;
9
+ constructor(outDir) {
10
+ this.filePath = datasetPath(outDir);
11
+ }
12
+ async open() {
13
+ await fs.mkdir(path.dirname(this.filePath), { recursive: true });
14
+ this.stream = createWriteStream(this.filePath, { flags: 'a', encoding: 'utf8' });
15
+ await new Promise((resolve, reject) => {
16
+ this.stream.once('open', () => resolve());
17
+ this.stream.once('error', reject);
18
+ });
19
+ }
20
+ async append(outDir, recording, transcript) {
21
+ if (!this.stream)
22
+ throw new StorageError('DatasetWriter not opened', this.filePath);
23
+ const relPath = path.relative(outDir, path.join(recordingDir(outDir, recording.recordedAt, recording.id), 'transcript.txt'));
24
+ const entry = {
25
+ id: `plaud:${recording.id}`,
26
+ title: recording.title ?? null,
27
+ recorded_at: recording.recordedAt,
28
+ duration_seconds: recording.duration,
29
+ language: recording.language ?? null,
30
+ text: transcript.fullText,
31
+ path: relPath,
32
+ segment_count: transcript.segments.length,
33
+ };
34
+ const line = JSON.stringify(entry) + '\n';
35
+ await new Promise((resolve, reject) => {
36
+ this.stream.write(line, err => (err ? reject(err) : resolve()));
37
+ });
38
+ }
39
+ async close() {
40
+ if (!this.stream)
41
+ return;
42
+ await new Promise((resolve, reject) => {
43
+ this.stream.end(() => resolve());
44
+ this.stream.once('error', reject);
45
+ });
46
+ this.stream = null;
47
+ }
48
+ get path() {
49
+ return this.filePath;
50
+ }
51
+ }
52
+ //# sourceMappingURL=dataset-writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset-writer.js","sourceRoot":"","sources":["../../src/storage/dataset-writer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAA;AACjC,OAAO,EAAE,iBAAiB,EAAoB,MAAM,SAAS,CAAA;AAC7D,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEtD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAa3C,MAAM,OAAO,aAAa;IACP,QAAQ,CAAQ;IACzB,MAAM,GAAuB,IAAI,CAAA;IAEzC,YAAY,MAAc;QACxB,IAAI,CAAC,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,CAAA;IACrC,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAChE,IAAI,CAAC,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAA;QAChF,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,IAAI,CAAC,MAAO,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAA;YAC1C,IAAI,CAAC,MAAO,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QACpC,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,MAAc,EAAE,SAAyB,EAAE,UAA2B;QACjF,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,YAAY,CAAC,0BAA0B,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QAEnF,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAC3B,MAAM,EACN,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,CAAC,EAAE,gBAAgB,CAAC,CACtF,CAAA;QAED,MAAM,KAAK,GAAiB;YAC1B,EAAE,EAAE,SAAS,SAAS,CAAC,EAAE,EAAE;YAC3B,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,IAAI;YAC9B,WAAW,EAAE,SAAS,CAAC,UAAU;YACjC,gBAAgB,EAAE,SAAS,CAAC,QAAQ;YACpC,QAAQ,EAAE,SAAS,CAAC,QAAQ,IAAI,IAAI;YACpC,IAAI,EAAE,UAAU,CAAC,QAAQ;YACzB,IAAI,EAAE,OAAO;YACb,aAAa,EAAE,UAAU,CAAC,QAAQ,CAAC,MAAM;SAC1C,CAAA;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAA;QAEzC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,IAAI,CAAC,MAAO,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;QAClE,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAM;QACxB,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,IAAI,CAAC,MAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAA;YACjC,IAAI,CAAC,MAAO,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QACpC,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;IACpB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,QAAQ,CAAA;IACtB,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ export declare function defaultOutDir(): string;
2
+ /** plaud/recordings/2026/02/2026-02-24T083012Z__plaud_<id>/ */
3
+ export declare function recordingDir(outDir: string, recordedAt: string, recordingId: string): string;
4
+ export declare function stateDir(outDir: string): string;
5
+ export declare function syncStatePath(outDir: string): string;
6
+ export declare function runLogsPath(outDir: string): string;
7
+ export declare function datasetPath(outDir: string): string;
8
+ export declare function authTokenPath(): string;
9
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/storage/paths.ts"],"names":[],"mappings":"AAGA,wBAAgB,aAAa,IAAI,MAAM,CAItC;AAED,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAO5F;AAQD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
@@ -0,0 +1,38 @@
1
+ import path from 'node:path';
2
+ import os from 'node:os';
3
+ export function defaultOutDir() {
4
+ const env = process.env['ALTA_DATA_DIR'];
5
+ if (env)
6
+ return path.resolve(env);
7
+ return path.join(os.homedir(), 'alta', 'data', 'plaud');
8
+ }
9
+ /** plaud/recordings/2026/02/2026-02-24T083012Z__plaud_<id>/ */
10
+ export function recordingDir(outDir, recordedAt, recordingId) {
11
+ const dt = new Date(recordedAt);
12
+ const year = String(dt.getUTCFullYear());
13
+ const month = String(dt.getUTCMonth() + 1).padStart(2, '0');
14
+ const timestamp = formatISOCompact(dt);
15
+ const dirName = `${timestamp}__plaud_${recordingId}`;
16
+ return path.join(outDir, 'recordings', year, month, dirName);
17
+ }
18
+ /** Format: 2026-02-24T083012Z */
19
+ function formatISOCompact(dt) {
20
+ const iso = dt.toISOString(); // 2026-02-24T08:30:12.000Z
21
+ return iso.replace(/[-:]/g, '').replace(/\.\d{3}/, '').replace('T', 'T').slice(0, 16) + 'Z';
22
+ }
23
+ export function stateDir(outDir) {
24
+ return path.join(outDir, '_state');
25
+ }
26
+ export function syncStatePath(outDir) {
27
+ return path.join(stateDir(outDir), 'sync_state.json');
28
+ }
29
+ export function runLogsPath(outDir) {
30
+ return path.join(stateDir(outDir), 'run_logs.ndjson');
31
+ }
32
+ export function datasetPath(outDir) {
33
+ return path.join(outDir, 'datasets', 'plaud_transcripts.jsonl');
34
+ }
35
+ export function authTokenPath() {
36
+ return path.join(os.homedir(), '.alta', 'plaud-auth.json');
37
+ }
38
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/storage/paths.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,MAAM,SAAS,CAAA;AAExB,MAAM,UAAU,aAAa;IAC3B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;IACxC,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACjC,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;AACzD,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,UAAkB,EAAE,WAAmB;IAClF,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAA;IAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,CAAA;IACxC,MAAM,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IAC3D,MAAM,SAAS,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAA;IACtC,MAAM,OAAO,GAAG,GAAG,SAAS,WAAW,WAAW,EAAE,CAAA;IACpD,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;AAC9D,CAAC;AAED,iCAAiC;AACjC,SAAS,gBAAgB,CAAC,EAAQ;IAChC,MAAM,GAAG,GAAG,EAAE,CAAC,WAAW,EAAE,CAAA,CAAC,2BAA2B;IACxD,OAAO,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAA;AAC7F,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAc;IACrC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AACpC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC,CAAA;AACvD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC,CAAA;AACvD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,yBAAyB,CAAC,CAAA;AACjE,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAA;AAC5D,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { PlaudRecording, PlaudTranscript } from '../client/types.js';
2
+ import type { ChecksumMismatchError } from '../errors.js';
3
+ import type { HttpClient } from '../client/http.js';
4
+ export type TranscriptFormat = 'json' | 'txt' | 'md';
5
+ export interface RecordingWriteResult {
6
+ dir: string;
7
+ hasAudio: boolean;
8
+ hasTranscript: boolean;
9
+ }
10
+ export declare class RecordingStore {
11
+ private readonly outDir;
12
+ constructor(outDir: string);
13
+ recordingDir(recording: PlaudRecording): string;
14
+ writeMetadata(recording: PlaudRecording): Promise<string>;
15
+ writeTranscript(recording: PlaudRecording, transcript: PlaudTranscript, formats?: TranscriptFormat[]): Promise<void>;
16
+ writeAudio(recording: PlaudRecording, http: HttpClient): Promise<boolean>;
17
+ private getAudioUrl;
18
+ writeAudioFromUrl(recording: PlaudRecording, url: string, http: HttpClient): Promise<boolean>;
19
+ writeChecksums(recording: PlaudRecording): Promise<void>;
20
+ verify(recording: PlaudRecording): Promise<ChecksumMismatchError[]>;
21
+ exists(recording: PlaudRecording): Promise<boolean>;
22
+ hasMissingFiles(recording: PlaudRecording): Promise<boolean>;
23
+ }
24
+ //# sourceMappingURL=recording-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recording-store.d.ts","sourceRoot":"","sources":["../../src/storage/recording-store.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAGnD,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,KAAK,GAAG,IAAI,CAAA;AAEpD,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,OAAO,CAAA;IACjB,aAAa,EAAE,OAAO,CAAA;CACvB;AAED,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAE3C,YAAY,CAAC,SAAS,EAAE,cAAc,GAAG,MAAM;IAIzC,aAAa,CAAC,SAAS,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAQzD,eAAe,CACnB,SAAS,EAAE,cAAc,EACzB,UAAU,EAAE,eAAe,EAC3B,OAAO,GAAE,gBAAgB,EAA0B,GAClD,OAAO,CAAC,IAAI,CAAC;IA0BV,UAAU,CAAC,SAAS,EAAE,cAAc,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC;YAmBjE,WAAW;IAMnB,iBAAiB,CAAC,SAAS,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC;IAiB7F,cAAc,CAAC,SAAS,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAMxD,MAAM,CAAC,SAAS,EAAE,cAAc,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAKnE,MAAM,CAAC,SAAS,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAUnD,eAAe,CAAC,SAAS,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;CAYnE"}
@@ -0,0 +1,161 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { writeFileAtomic, writeStreamAtomic } from './atomic.js';
4
+ import { writeChecksumManifest, verifyChecksums } from './checksums.js';
5
+ import { toPlainText, toMarkdown } from '../transcript/formatter.js';
6
+ import { recordingDir } from './paths.js';
7
+ import { getLogger } from '../logger.js';
8
+ export class RecordingStore {
9
+ outDir;
10
+ constructor(outDir) {
11
+ this.outDir = outDir;
12
+ }
13
+ recordingDir(recording) {
14
+ return recordingDir(this.outDir, recording.recordedAt, recording.id);
15
+ }
16
+ async writeMetadata(recording) {
17
+ const dir = this.recordingDir(recording);
18
+ const meta = buildMetaJson(recording);
19
+ await writeFileAtomic(path.join(dir, 'meta.json'), JSON.stringify(meta, null, 2));
20
+ getLogger().debug({ recordingId: recording.id, dir }, 'Wrote meta.json');
21
+ return dir;
22
+ }
23
+ async writeTranscript(recording, transcript, formats = ['json', 'txt', 'md']) {
24
+ const dir = this.recordingDir(recording);
25
+ if (formats.includes('json')) {
26
+ const json = {
27
+ recordingId: transcript.recordingId,
28
+ language: transcript.language,
29
+ duration: transcript.duration,
30
+ segments: transcript.segments,
31
+ };
32
+ await writeFileAtomic(path.join(dir, 'transcript.json'), JSON.stringify(json, null, 2));
33
+ }
34
+ if (formats.includes('txt')) {
35
+ const txt = toPlainText(transcript);
36
+ await writeFileAtomic(path.join(dir, 'transcript.txt'), txt);
37
+ }
38
+ if (formats.includes('md')) {
39
+ const md = toMarkdown(transcript, recording);
40
+ await writeFileAtomic(path.join(dir, 'transcript.md'), md);
41
+ }
42
+ getLogger().debug({ recordingId: recording.id, formats }, 'Wrote transcript files');
43
+ }
44
+ async writeAudio(recording, http) {
45
+ const url = await this.getAudioUrl(recording, http);
46
+ if (!url)
47
+ return false;
48
+ const dir = this.recordingDir(recording);
49
+ const ext = guessAudioExtension(recording.mimeType);
50
+ const destPath = path.join(dir, `audio.${ext}`);
51
+ try {
52
+ const stream = await http.getStream(url);
53
+ await writeStreamAtomic(destPath, stream);
54
+ getLogger().debug({ recordingId: recording.id, path: destPath }, 'Wrote audio file');
55
+ return true;
56
+ }
57
+ catch (err) {
58
+ getLogger().warn({ recordingId: recording.id, err }, 'Failed to download audio');
59
+ return false;
60
+ }
61
+ }
62
+ async getAudioUrl(recording, http) {
63
+ // This is a stub — the PlaudApiClient resolves the actual URL
64
+ // RecordingStore receives the already-resolved URL via writeAudioFromUrl
65
+ return null;
66
+ }
67
+ async writeAudioFromUrl(recording, url, http) {
68
+ const dir = this.recordingDir(recording);
69
+ const ext = guessAudioExtension(recording.mimeType);
70
+ const destPath = path.join(dir, `audio.${ext}`);
71
+ try {
72
+ // S3 presigned URLs must be fetched without Plaud auth headers
73
+ const stream = await http.downloadExternalUrl(url);
74
+ await writeStreamAtomic(destPath, stream);
75
+ getLogger().debug({ recordingId: recording.id, path: destPath }, 'Wrote audio file');
76
+ return true;
77
+ }
78
+ catch (err) {
79
+ getLogger().warn({ recordingId: recording.id, err }, 'Failed to download audio');
80
+ return false;
81
+ }
82
+ }
83
+ async writeChecksums(recording) {
84
+ const dir = this.recordingDir(recording);
85
+ await writeChecksumManifest(dir, recording.id);
86
+ getLogger().debug({ recordingId: recording.id }, 'Wrote checksums.json');
87
+ }
88
+ async verify(recording) {
89
+ const dir = this.recordingDir(recording);
90
+ return verifyChecksums(dir);
91
+ }
92
+ async exists(recording) {
93
+ const dir = this.recordingDir(recording);
94
+ try {
95
+ await fs.access(dir);
96
+ return true;
97
+ }
98
+ catch {
99
+ return false;
100
+ }
101
+ }
102
+ async hasMissingFiles(recording) {
103
+ const dir = this.recordingDir(recording);
104
+ const required = ['meta.json'];
105
+ for (const file of required) {
106
+ try {
107
+ await fs.access(path.join(dir, file));
108
+ }
109
+ catch {
110
+ return true;
111
+ }
112
+ }
113
+ return false;
114
+ }
115
+ }
116
+ function buildMetaJson(recording) {
117
+ return {
118
+ source: 'plaud',
119
+ source_recording_id: recording.id,
120
+ recorded_at: recording.recordedAt,
121
+ imported_at: new Date().toISOString(),
122
+ title: recording.title,
123
+ duration_seconds: recording.duration,
124
+ language: recording.language,
125
+ audio: recording.fileSize
126
+ ? {
127
+ filename: `audio.${guessAudioExtension(recording.mimeType)}`,
128
+ mime: recording.mimeType,
129
+ bytes: recording.fileSize,
130
+ }
131
+ : null,
132
+ transcript: {
133
+ has_timestamps: true, // will be updated after writing
134
+ format: 'segments',
135
+ filename_json: 'transcript.json',
136
+ filename_txt: 'transcript.txt',
137
+ filename_md: 'transcript.md',
138
+ },
139
+ integrity: {
140
+ dedupe_key: `plaud:${recording.id}`,
141
+ },
142
+ tags: recording.tags,
143
+ folder_id: recording.folderId,
144
+ device_id: recording.deviceId,
145
+ summary: recording.summary,
146
+ };
147
+ }
148
+ function guessAudioExtension(mimeType) {
149
+ if (mimeType.includes('m4a') || mimeType.includes('mp4'))
150
+ return 'm4a';
151
+ if (mimeType.includes('wav'))
152
+ return 'wav';
153
+ if (mimeType.includes('mp3'))
154
+ return 'mp3';
155
+ if (mimeType.includes('ogg'))
156
+ return 'ogg';
157
+ if (mimeType.includes('webm'))
158
+ return 'webm';
159
+ return 'm4a'; // default
160
+ }
161
+ //# sourceMappingURL=recording-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recording-store.js","sourceRoot":"","sources":["../../src/storage/recording-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAA;AACjC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAChE,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AACvE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAIzC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAUxC,MAAM,OAAO,cAAc;IACI;IAA7B,YAA6B,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IAAG,CAAC;IAE/C,YAAY,CAAC,SAAyB;QACpC,OAAO,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,CAAC,CAAA;IACtE,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,SAAyB;QAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAA;QACxC,MAAM,IAAI,GAAG,aAAa,CAAC,SAAS,CAAC,CAAA;QACrC,MAAM,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;QACjF,SAAS,EAAE,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,iBAAiB,CAAC,CAAA;QACxE,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,KAAK,CAAC,eAAe,CACnB,SAAyB,EACzB,UAA2B,EAC3B,UAA8B,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC;QAEnD,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAA;QAExC,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG;gBACX,WAAW,EAAE,UAAU,CAAC,WAAW;gBACnC,QAAQ,EAAE,UAAU,CAAC,QAAQ;gBAC7B,QAAQ,EAAE,UAAU,CAAC,QAAQ;gBAC7B,QAAQ,EAAE,UAAU,CAAC,QAAQ;aAC9B,CAAA;YACD,MAAM,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;QACzF,CAAC;QAED,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,WAAW,CAAC,UAAU,CAAC,CAAA;YACnC,MAAM,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,EAAE,GAAG,CAAC,CAAA;QAC9D,CAAC;QAED,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,MAAM,EAAE,GAAG,UAAU,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;YAC5C,MAAM,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,EAAE,EAAE,CAAC,CAAA;QAC5D,CAAC;QAED,SAAS,EAAE,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,wBAAwB,CAAC,CAAA;IACrF,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,SAAyB,EAAE,IAAgB;QAC1D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;QACnD,IAAI,CAAC,GAAG;YAAE,OAAO,KAAK,CAAA;QAEtB,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAA;QACxC,MAAM,GAAG,GAAG,mBAAmB,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,GAAG,EAAE,CAAC,CAAA;QAE/C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;YACxC,MAAM,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;YACzC,SAAS,EAAE,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,kBAAkB,CAAC,CAAA;YACpF,OAAO,IAAI,CAAA;QACb,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,0BAA0B,CAAC,CAAA;YAChF,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,SAAyB,EAAE,IAAgB;QACnE,8DAA8D;QAC9D,yEAAyE;QACzE,OAAO,IAAI,CAAA;IACb,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,SAAyB,EAAE,GAAW,EAAE,IAAgB;QAC9E,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAA;QACxC,MAAM,GAAG,GAAG,mBAAmB,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,GAAG,EAAE,CAAC,CAAA;QAE/C,IAAI,CAAC;YACH,+DAA+D;YAC/D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAA;YAClD,MAAM,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;YACzC,SAAS,EAAE,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,kBAAkB,CAAC,CAAA;YACpF,OAAO,IAAI,CAAA;QACb,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,0BAA0B,CAAC,CAAA;YAChF,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,SAAyB;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAA;QACxC,MAAM,qBAAqB,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,CAAC,CAAA;QAC9C,SAAS,EAAE,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,EAAE,EAAE,sBAAsB,CAAC,CAAA;IAC1E,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,SAAyB;QACpC,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAA;QACxC,OAAO,eAAe,CAAC,GAAG,CAAC,CAAA;IAC7B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,SAAyB;QACpC,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAA;QACxC,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACpB,OAAO,IAAI,CAAA;QACb,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,SAAyB;QAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAA;QACxC,MAAM,QAAQ,GAAG,CAAC,WAAW,CAAC,CAAA;QAC9B,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAA;YACvC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAA;YACb,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;CACF;AAED,SAAS,aAAa,CAAC,SAAyB;IAC9C,OAAO;QACL,MAAM,EAAE,OAAO;QACf,mBAAmB,EAAE,SAAS,CAAC,EAAE;QACjC,WAAW,EAAE,SAAS,CAAC,UAAU;QACjC,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,KAAK,EAAE,SAAS,CAAC,KAAK;QACtB,gBAAgB,EAAE,SAAS,CAAC,QAAQ;QACpC,QAAQ,EAAE,SAAS,CAAC,QAAQ;QAC5B,KAAK,EAAE,SAAS,CAAC,QAAQ;YACvB,CAAC,CAAC;gBACE,QAAQ,EAAE,SAAS,mBAAmB,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE;gBAC5D,IAAI,EAAE,SAAS,CAAC,QAAQ;gBACxB,KAAK,EAAE,SAAS,CAAC,QAAQ;aAC1B;YACH,CAAC,CAAC,IAAI;QACR,UAAU,EAAE;YACV,cAAc,EAAE,IAAI,EAAE,gCAAgC;YACtD,MAAM,EAAE,UAAU;YAClB,aAAa,EAAE,iBAAiB;YAChC,YAAY,EAAE,gBAAgB;YAC9B,WAAW,EAAE,eAAe;SAC7B;QACD,SAAS,EAAE;YACT,UAAU,EAAE,SAAS,SAAS,CAAC,EAAE,EAAE;SACpC;QACD,IAAI,EAAE,SAAS,CAAC,IAAI;QACpB,SAAS,EAAE,SAAS,CAAC,QAAQ;QAC7B,SAAS,EAAE,SAAS,CAAC,QAAQ;QAC7B,OAAO,EAAE,SAAS,CAAC,OAAO;KAC3B,CAAA;AACH,CAAC;AAED,SAAS,mBAAmB,CAAC,QAAgB;IAC3C,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACtE,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAC1C,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAC1C,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAC1C,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAA;IAC5C,OAAO,KAAK,CAAA,CAAC,UAAU;AACzB,CAAC"}
@@ -0,0 +1,21 @@
1
+ export interface QueueResult<T> {
2
+ succeeded: T[];
3
+ failed: Array<{
4
+ item: T;
5
+ error: Error;
6
+ }>;
7
+ }
8
+ /**
9
+ * Process items with bounded concurrency.
10
+ * Uses a simple semaphore — no extra dependencies.
11
+ */
12
+ export declare function processQueue<T>(items: T[], processor: (item: T) => Promise<void>, concurrency: number): Promise<QueueResult<T>>;
13
+ /**
14
+ * Retry a function with exponential backoff.
15
+ * Only retries if the error is retryable (isRetryable === true for ApiError).
16
+ */
17
+ export declare function retryWithBackoff<T>(fn: () => Promise<T>, opts?: {
18
+ maxAttempts?: number;
19
+ label?: string;
20
+ }): Promise<T>;
21
+ //# sourceMappingURL=download-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download-queue.d.ts","sourceRoot":"","sources":["../../src/sync/download-queue.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,SAAS,EAAE,CAAC,EAAE,CAAA;IACd,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE,CAAC,CAAA;CACzC;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,KAAK,EAAE,CAAC,EAAE,EACV,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,EACrC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAuCzB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAClD,OAAO,CAAC,CAAC,CAAC,CA2BZ"}
@@ -0,0 +1,82 @@
1
+ import { ApiError } from '../errors.js';
2
+ import { getLogger } from '../logger.js';
3
+ /**
4
+ * Process items with bounded concurrency.
5
+ * Uses a simple semaphore — no extra dependencies.
6
+ */
7
+ export async function processQueue(items, processor, concurrency) {
8
+ const succeeded = [];
9
+ const failed = [];
10
+ let index = 0;
11
+ let active = 0;
12
+ let resolve = null;
13
+ const tick = async () => {
14
+ while (active < concurrency && index < items.length) {
15
+ const item = items[index++];
16
+ active++;
17
+ (async () => {
18
+ try {
19
+ await processor(item);
20
+ succeeded.push(item);
21
+ }
22
+ catch (err) {
23
+ failed.push({ item, error: err instanceof Error ? err : new Error(String(err)) });
24
+ }
25
+ finally {
26
+ active--;
27
+ if (resolve) {
28
+ const r = resolve;
29
+ resolve = null;
30
+ r();
31
+ }
32
+ }
33
+ })().catch(() => undefined);
34
+ }
35
+ };
36
+ await tick();
37
+ while (active > 0 || index < items.length) {
38
+ await new Promise(r => { resolve = r; });
39
+ await tick();
40
+ }
41
+ return { succeeded, failed };
42
+ }
43
+ /**
44
+ * Retry a function with exponential backoff.
45
+ * Only retries if the error is retryable (isRetryable === true for ApiError).
46
+ */
47
+ export async function retryWithBackoff(fn, opts = {}) {
48
+ const maxAttempts = opts.maxAttempts ?? 4;
49
+ const delays = [0, 1000, 4000, 16000];
50
+ const log = getLogger();
51
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
52
+ try {
53
+ return await fn();
54
+ }
55
+ catch (err) {
56
+ const isLast = attempt === maxAttempts;
57
+ const retryable = isRetryableError(err);
58
+ if (isLast || !retryable) {
59
+ if (attempt > 1) {
60
+ log.warn({ attempt, label: opts.label, err }, 'Giving up after retries');
61
+ }
62
+ throw err;
63
+ }
64
+ const delay = delays[attempt] ?? 16000;
65
+ log.debug({ attempt, delay, label: opts.label }, 'Retrying after delay');
66
+ await sleep(delay);
67
+ }
68
+ }
69
+ // Unreachable — TypeScript needs this
70
+ throw new Error('Retry loop exhausted');
71
+ }
72
+ function isRetryableError(err) {
73
+ if (err instanceof ApiError)
74
+ return err.isRetryable;
75
+ if (err instanceof Error && err.message.includes('429'))
76
+ return true;
77
+ return false;
78
+ }
79
+ function sleep(ms) {
80
+ return new Promise(resolve => setTimeout(resolve, ms));
81
+ }
82
+ //# sourceMappingURL=download-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download-queue.js","sourceRoot":"","sources":["../../src/sync/download-queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAOxC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAU,EACV,SAAqC,EACrC,WAAmB;IAEnB,MAAM,SAAS,GAAQ,EAAE,CAAA;IACzB,MAAM,MAAM,GAAqC,EAAE,CAAA;IAEnD,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,MAAM,GAAG,CAAC,CAAA;IACd,IAAI,OAAO,GAAwB,IAAI,CAAA;IAEvC,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE;QACrC,OAAO,MAAM,GAAG,WAAW,IAAI,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YACpD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAE,CAAE,CAAA;YAC5B,MAAM,EAAE,CAEP;YAAA,CAAC,KAAK,IAAI,EAAE;gBACX,IAAI,CAAC;oBACH,MAAM,SAAS,CAAC,IAAI,CAAC,CAAA;oBACrB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBACtB,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAA;gBACnF,CAAC;wBAAS,CAAC;oBACT,MAAM,EAAE,CAAA;oBACR,IAAI,OAAO,EAAE,CAAC;wBACZ,MAAM,CAAC,GAAG,OAAO,CAAA;wBACjB,OAAO,GAAG,IAAI,CAAA;wBACd,CAAC,EAAE,CAAA;oBACL,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC,CAAA;IAED,MAAM,IAAI,EAAE,CAAA;IAEZ,OAAO,MAAM,GAAG,CAAC,IAAI,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAC1C,MAAM,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,GAAG,OAAO,GAAG,CAAC,CAAA,CAAC,CAAC,CAAC,CAAA;QAC7C,MAAM,IAAI,EAAE,CAAA;IACd,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAA;AAC9B,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,EAAoB,EACpB,OAAiD,EAAE;IAEnD,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,CAAC,CAAA;IACzC,MAAM,MAAM,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;IACrC,MAAM,GAAG,GAAG,SAAS,EAAE,CAAA;IAEvB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QACxD,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAA;QACnB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,MAAM,GAAG,OAAO,KAAK,WAAW,CAAA;YACtC,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;YAEvC,IAAI,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;gBACzB,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;oBAChB,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,yBAAyB,CAAC,CAAA;gBAC1E,CAAC;gBACD,MAAM,GAAG,CAAA;YACX,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,KAAK,CAAA;YACtC,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,sBAAsB,CAAC,CAAA;YACxE,MAAM,KAAK,CAAC,KAAK,CAAC,CAAA;QACpB,CAAC;IACH,CAAC;IAED,sCAAsC;IACtC,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAA;AACzC,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAY;IACpC,IAAI,GAAG,YAAY,QAAQ;QAAE,OAAO,GAAG,CAAC,WAAW,CAAA;IACnD,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACpE,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAA;AACxD,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { type RecordingState } from './types.js';
2
+ import type { PlaudRecording } from '../client/types.js';
3
+ export declare class IncrementalTracker {
4
+ private state;
5
+ load(outDir: string): Promise<void>;
6
+ persist(outDir: string): Promise<void>;
7
+ markSuccessfulSync(): void;
8
+ getSince(): Date | undefined;
9
+ needsDownload(recording: PlaudRecording): boolean;
10
+ computeContentHash(recording: PlaudRecording): string;
11
+ markComplete(recordingId: string, recordedAt: string, opts: {
12
+ hasAudio: boolean;
13
+ hasTranscript: boolean;
14
+ contentHash: string;
15
+ }): void;
16
+ markVerified(recordingId: string): void;
17
+ getRecordingState(recordingId: string): RecordingState | undefined;
18
+ getAllRecordingIds(): string[];
19
+ get lastSuccessfulSyncAt(): string | undefined;
20
+ }
21
+ //# sourceMappingURL=incremental.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"incremental.d.ts","sourceRoot":"","sources":["../../src/sync/incremental.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmC,KAAK,cAAc,EAAE,MAAM,YAAY,CAAA;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAGxD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,KAAK,CAGZ;IAEK,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBnC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5C,kBAAkB,IAAI,IAAI;IAI1B,QAAQ,IAAI,IAAI,GAAG,SAAS;IAK5B,aAAa,CAAC,SAAS,EAAE,cAAc,GAAG,OAAO;IAcjD,kBAAkB,CAAC,SAAS,EAAE,cAAc,GAAG,MAAM;IAYrD,YAAY,CACV,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,aAAa,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GACvE,IAAI;IAWP,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAQvC,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAIlE,kBAAkB,IAAI,MAAM,EAAE;IAI9B,IAAI,oBAAoB,IAAI,MAAM,GAAG,SAAS,CAE7C;CACF"}