@eldrforge/audio-tools 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -27,25 +27,244 @@ import { transcribeAudio as transcribeAudio$1 } from '@eldrforge/ai-service';
27
27
  };
28
28
  /* eslint-enable no-console */ }
29
29
 
30
+ function _define_property(obj, key, value) {
31
+ if (key in obj) {
32
+ Object.defineProperty(obj, key, {
33
+ value: value,
34
+ enumerable: true,
35
+ configurable: true,
36
+ writable: true
37
+ });
38
+ } else {
39
+ obj[key] = value;
40
+ }
41
+ return obj;
42
+ }
30
43
  /**
31
- * Display countdown before recording
32
- * @param seconds Number of seconds to count down
33
- * @param onTick Optional callback called on each tick
34
- */ async function countdown(seconds, onTick) {
35
- const logger = getLogger();
36
- for(let i = seconds; i > 0; i--){
37
- logger.info(`Recording in ${i}...`);
38
- if (onTick) {
39
- onTick(i);
44
+ * ANSI escape codes for terminal control
45
+ */ const ANSI = {
46
+ // Cursor movement
47
+ CURSOR_UP: '\x1b[1A',
48
+ CURSOR_TO_START: '\x1b[0G',
49
+ CLEAR_LINE: '\x1b[2K',
50
+ // Colors
51
+ RED: '\x1b[31m',
52
+ CYAN: '\x1b[36m',
53
+ RESET: '\x1b[0m',
54
+ // Text styles
55
+ BOLD: '\x1b[1m'};
56
+ /**
57
+ * Format seconds into MM:SS format
58
+ */ function formatTime(seconds) {
59
+ const minutes = Math.floor(seconds / 60);
60
+ const remainingSeconds = seconds % 60;
61
+ return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
62
+ }
63
+ /**
64
+ * Generate a beep sound using process.stdout.write with ASCII bell character
65
+ */ function beep() {
66
+ process.stdout.write('\x07'); // ASCII bell character
67
+ }
68
+ /**
69
+ * Check if terminal supports colors and cursor movement
70
+ */ function supportsAnsi() {
71
+ return process.stdout.isTTY && process.env.TERM !== 'dumb' && !process.env.NO_COLOR;
72
+ }
73
+ /**
74
+ * Display a live countdown timer that updates in place
75
+ */ class CountdownTimer {
76
+ /**
77
+ * Start the countdown timer
78
+ */ start() {
79
+ return new Promise((resolve)=>{
80
+ // Handle zero seconds case
81
+ if (this.currentSeconds <= 0) {
82
+ this.options.onComplete();
83
+ resolve();
84
+ return;
85
+ }
86
+ // Display initial countdown
87
+ this.displayCountdown();
88
+ this.intervalId = setInterval(()=>{
89
+ // Check if destroyed before processing
90
+ if (this.isDestroyed) {
91
+ clearInterval(this.intervalId);
92
+ resolve();
93
+ return;
94
+ }
95
+ this.currentSeconds--;
96
+ // Check for beep warning
97
+ if (this.options.beepAt30Seconds && this.currentSeconds === 30 && !this.hasBeepedAt30) {
98
+ beep();
99
+ this.hasBeepedAt30 = true;
100
+ }
101
+ // Call tick callback
102
+ this.options.onTick(this.currentSeconds);
103
+ if (this.currentSeconds <= 0) {
104
+ this.stop();
105
+ this.options.onComplete();
106
+ resolve();
107
+ } else {
108
+ this.displayCountdown();
109
+ }
110
+ }, 1000);
111
+ });
112
+ }
113
+ /**
114
+ * Stop the countdown timer
115
+ */ stop() {
116
+ if (this.isDestroyed) {
117
+ return;
118
+ }
119
+ if (this.intervalId) {
120
+ clearInterval(this.intervalId);
121
+ this.intervalId = null;
122
+ }
123
+ if (this.options.clearOnComplete && this.supportsAnsi) {
124
+ // Clear the countdown line
125
+ process.stdout.write(ANSI.CURSOR_TO_START + ANSI.CLEAR_LINE);
126
+ } else if (!this.isFirstDisplay) {
127
+ // Add a newline if we've been updating in place
128
+ process.stdout.write('\n');
129
+ }
130
+ }
131
+ /**
132
+ * Get current remaining time
133
+ */ getRemainingSeconds() {
134
+ return this.currentSeconds;
135
+ }
136
+ /**
137
+ * Display the countdown timer
138
+ */ displayCountdown() {
139
+ const timeString = formatTime(this.currentSeconds);
140
+ const isWarningTime = this.currentSeconds <= 30;
141
+ let output;
142
+ if (this.supportsAnsi) {
143
+ // Use colors and in-place updating if supported
144
+ if (!this.isFirstDisplay) {
145
+ // Move cursor up and clear the line to overwrite previous countdown
146
+ process.stdout.write(ANSI.CURSOR_UP + ANSI.CURSOR_TO_START + ANSI.CLEAR_LINE);
147
+ }
148
+ const color = isWarningTime && this.options.redAt30Seconds ? ANSI.RED : ANSI.CYAN;
149
+ const style = isWarningTime ? ANSI.BOLD : '';
150
+ output = `${color}${style}⏱️ Recording time remaining: ${timeString}${ANSI.RESET}`;
151
+ } else {
152
+ // Fallback for terminals that don't support ANSI
153
+ const warning = isWarningTime ? ' ⚠️ ' : '';
154
+ output = `⏱️ Recording time remaining: ${timeString}${warning}`;
155
+ }
156
+ process.stdout.write(output + '\n');
157
+ this.isFirstDisplay = false;
158
+ }
159
+ /**
160
+ * Set up cleanup handlers for process termination and uncaught exceptions
161
+ */ setupCleanupHandlers() {
162
+ // Skip setting up process listeners in test environments to avoid listener leaks
163
+ if (process.env.NODE_ENV === 'test' || process.env.VITEST) {
164
+ return;
40
165
  }
41
- await sleep(1000);
166
+ const cleanup = ()=>{
167
+ this.destroy();
168
+ };
169
+ // Handle various exit scenarios
170
+ const exitHandler = ()=>cleanup();
171
+ const uncaughtExceptionHandler = (error)=>{
172
+ cleanup();
173
+ // Re-throw to maintain normal error handling
174
+ throw error;
175
+ };
176
+ process.on('exit', exitHandler);
177
+ process.on('SIGINT', exitHandler);
178
+ process.on('SIGTERM', exitHandler);
179
+ process.on('uncaughtException', uncaughtExceptionHandler);
180
+ process.on('unhandledRejection', cleanup);
181
+ // Store handlers for removal during destroy
182
+ this.cleanupHandlers = [
183
+ ()=>process.removeListener('exit', exitHandler),
184
+ ()=>process.removeListener('SIGINT', exitHandler),
185
+ ()=>process.removeListener('SIGTERM', exitHandler),
186
+ ()=>process.removeListener('uncaughtException', uncaughtExceptionHandler),
187
+ ()=>process.removeListener('unhandledRejection', cleanup)
188
+ ];
189
+ }
190
+ /**
191
+ * Destroy the timer and clean up all resources
192
+ */ destroy() {
193
+ if (this.isDestroyed) {
194
+ return;
195
+ }
196
+ this.isDestroyed = true;
197
+ this.stop();
198
+ // Remove all process event listeners
199
+ this.cleanupHandlers.forEach((handler)=>{
200
+ try {
201
+ handler();
202
+ } catch {
203
+ // Ignore errors during cleanup
204
+ }
205
+ });
206
+ this.cleanupHandlers = [];
207
+ }
208
+ /**
209
+ * Check if the timer has been destroyed
210
+ */ isTimerDestroyed() {
211
+ return this.isDestroyed;
212
+ }
213
+ constructor(options){
214
+ _define_property(this, "options", void 0);
215
+ _define_property(this, "intervalId", null);
216
+ _define_property(this, "currentSeconds", void 0);
217
+ _define_property(this, "hasBeepedAt30", false);
218
+ _define_property(this, "isFirstDisplay", true);
219
+ _define_property(this, "supportsAnsi", void 0);
220
+ _define_property(this, "cleanupHandlers", []);
221
+ _define_property(this, "isDestroyed", false);
222
+ this.options = {
223
+ beepAt30Seconds: true,
224
+ redAt30Seconds: true,
225
+ onTick: ()=>{},
226
+ onComplete: ()=>{},
227
+ clearOnComplete: false,
228
+ ...options
229
+ };
230
+ this.currentSeconds = this.options.durationSeconds;
231
+ this.supportsAnsi = supportsAnsi();
232
+ // Set up cleanup handlers for process termination
233
+ this.setupCleanupHandlers();
42
234
  }
43
- logger.info('Recording!');
44
235
  }
45
236
  /**
46
- * Sleep for specified milliseconds
47
- */ function sleep(ms) {
48
- return new Promise((resolve)=>setTimeout(resolve, ms));
237
+ * Create and start a countdown timer (convenience function)
238
+ */ async function startCountdown(options) {
239
+ const timer = new CountdownTimer(options);
240
+ return timer.start();
241
+ }
242
+ /**
243
+ * Create a countdown timer for audio recording with sensible defaults
244
+ */ function createAudioRecordingCountdown(durationSeconds) {
245
+ return new CountdownTimer({
246
+ durationSeconds,
247
+ beepAt30Seconds: true,
248
+ redAt30Seconds: true,
249
+ clearOnComplete: true
250
+ });
251
+ }
252
+ /**
253
+ * Simple countdown function for backwards compatibility
254
+ * @param seconds Number of seconds to count down
255
+ * @param onTick Optional callback called on each tick
256
+ * @deprecated Use CountdownTimer or startCountdown for more features
257
+ */ async function countdown(seconds, onTick) {
258
+ const options = {
259
+ durationSeconds: seconds,
260
+ beepAt30Seconds: false,
261
+ redAt30Seconds: false,
262
+ clearOnComplete: false
263
+ };
264
+ if (onTick) {
265
+ options.onTick = onTick;
266
+ }
267
+ await startCountdown(options);
49
268
  }
50
269
 
51
270
  /**
@@ -122,24 +341,72 @@ import { transcribeAudio as transcribeAudio$1 } from '@eldrforge/ai-service';
122
341
  }
123
342
  }
124
343
  /**
125
- * Archive audio file to specified directory
126
- */ async function archiveAudio(audioPath, archiveDir, filename) {
344
+ * Get timestamped filename for archiving
345
+ */ function getTimestampedFilename(baseName, extension = '.json') {
346
+ const now = new Date();
347
+ // Format as YYMMdd-HHmm (e.g., 250701-1030)
348
+ const yy = now.getFullYear().toString().slice(-2);
349
+ const mm = (now.getMonth() + 1).toString().padStart(2, '0');
350
+ const dd = now.getDate().toString().padStart(2, '0');
351
+ const hh = now.getHours().toString().padStart(2, '0');
352
+ const min = now.getMinutes().toString().padStart(2, '0');
353
+ const timestamp = `${yy}${mm}${dd}-${hh}${min}`;
354
+ return `${timestamp}-${baseName}${extension}`;
355
+ }
356
+ /**
357
+ * Get timestamped filename for archived audio
358
+ */ function getTimestampedArchivedAudioFilename(originalExtension = '.wav') {
359
+ return getTimestampedFilename('review-audio', originalExtension);
360
+ }
361
+ /**
362
+ * Get timestamped filename for archived transcript
363
+ */ function getTimestampedArchivedTranscriptFilename() {
364
+ return getTimestampedFilename('review-transcript', '.md');
365
+ }
366
+ /**
367
+ * Archive audio file with transcription to specified directory
368
+ * This saves BOTH the audio file AND transcription text together
369
+ * @param originalAudioPath Path to the original audio file
370
+ * @param transcriptionText The transcribed text content
371
+ * @param outputDirectory Directory to save archived files (default: 'output')
372
+ * @returns Paths to both archived audio and transcript files
373
+ */ async function archiveAudio(originalAudioPath, transcriptionText, outputDirectory = 'output') {
127
374
  const logger = getLogger();
375
+ const path = await import('path');
128
376
  try {
129
- // Ensure archive directory exists
130
- await promises.mkdir(archiveDir, {
377
+ // Ensure the output directory exists
378
+ await promises.mkdir(outputDirectory, {
131
379
  recursive: true
132
380
  });
133
- // Determine archive filename
134
- const basename = filename || `audio-${Date.now()}.wav`;
135
- const archivePath = join(archiveDir, basename);
136
- // Copy file to archive
137
- await promises.copyFile(audioPath, archivePath);
138
- logger.info(`Audio archived to: ${archivePath}`);
139
- return archivePath;
381
+ // Get file extension from original audio file
382
+ const originalExtension = path.extname(originalAudioPath);
383
+ // Generate timestamped filenames
384
+ const archivedAudioFilename = getTimestampedArchivedAudioFilename(originalExtension);
385
+ const archivedTranscriptFilename = getTimestampedArchivedTranscriptFilename();
386
+ // Full paths for archived files
387
+ const archivedAudioPath = path.join(outputDirectory, archivedAudioFilename);
388
+ const archivedTranscriptPath = path.join(outputDirectory, archivedTranscriptFilename);
389
+ // Copy audio file if it exists
390
+ try {
391
+ await promises.access(originalAudioPath);
392
+ const audioBuffer = await promises.readFile(originalAudioPath);
393
+ await promises.writeFile(archivedAudioPath, audioBuffer);
394
+ logger.debug('Archived audio file to: %s', archivedAudioPath);
395
+ } catch {
396
+ logger.warn('AUDIO_FILE_NOT_FOUND: Original audio file not accessible | Path: %s | Impact: Cannot archive original', originalAudioPath);
397
+ }
398
+ // Save transcription text
399
+ const transcriptContent = `# Audio Transcription Archive\n\n**Original Audio File:** ${originalAudioPath}\n**Archived:** ${new Date().toISOString()}\n\n## Transcription\n\n${transcriptionText}`;
400
+ await promises.writeFile(archivedTranscriptPath, transcriptContent, 'utf8');
401
+ logger.debug('Archived transcription to: %s', archivedTranscriptPath);
402
+ logger.info('AUDIO_ARCHIVED: Audio and transcript archived successfully | Audio: %s | Transcript: %s | Status: archived', archivedAudioFilename, archivedTranscriptFilename);
403
+ return {
404
+ audioPath: archivedAudioPath,
405
+ transcriptPath: archivedTranscriptPath
406
+ };
140
407
  } catch (error) {
141
- logger.error('Failed to archive audio:', error);
142
- throw new Error(`Archive failed: ${error}`);
408
+ logger.error('AUDIO_ARCHIVE_FAILED: Failed to archive audio files | Error: %s | Impact: Audio not preserved', error.message);
409
+ throw new Error(`Audio archiving failed: ${error.message}`);
143
410
  }
144
411
  }
145
412
  /**
@@ -181,5 +448,5 @@ import { transcribeAudio as transcribeAudio$1 } from '@eldrforge/ai-service';
181
448
  }
182
449
  }
183
450
 
184
- export { archiveAudio, countdown, deleteAudio, findDevice, getAudioDuration, getDefaultDevice, getLogger, listAudioDevices, recordAudio, selectDeviceInteractive, setLogger, transcribeAudio };
451
+ export { CountdownTimer, archiveAudio, countdown, createAudioRecordingCountdown, deleteAudio, findDevice, getAudioDuration, getDefaultDevice, getLogger, getTimestampedArchivedAudioFilename, getTimestampedArchivedTranscriptFilename, listAudioDevices, recordAudio, selectDeviceInteractive, setLogger, startCountdown, transcribeAudio };
185
452
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/logger.ts","../src/countdown.ts","../src/devices.ts","../src/recording.ts","../src/transcription.ts"],"sourcesContent":["/**\n * Logger utilities for audio-tools\n */\n\nimport type { Logger } from './types';\n\nlet logger: Logger | undefined;\n\n/**\n * Set the logger instance\n */\nexport function setLogger(newLogger: Logger): void {\n logger = newLogger;\n}\n\n/**\n * Get the current logger or create a console fallback\n */\nexport function getLogger(): Logger {\n if (logger) {\n return logger;\n }\n\n // Console fallback\n /* eslint-disable no-console */\n return {\n error: (message: string, ...args: any[]) => console.error(message, ...args),\n warn: (message: string, ...args: any[]) => console.warn(message, ...args),\n info: (message: string, ...args: any[]) => console.log(message, ...args),\n debug: (message: string, ...args: any[]) => console.debug(message, ...args),\n };\n /* eslint-enable no-console */\n}\n\n","/**\n * Countdown timer for audio recording\n */\n\nimport { getLogger } from './logger';\n\n/**\n * Display countdown before recording\n * @param seconds Number of seconds to count down\n * @param onTick Optional callback called on each tick\n */\nexport async function countdown(\n seconds: number,\n onTick?: (remaining: number) => void\n): Promise<void> {\n const logger = getLogger();\n\n for (let i = seconds; i > 0; i--) {\n logger.info(`Recording in ${i}...`);\n\n if (onTick) {\n onTick(i);\n }\n\n await sleep(1000);\n }\n\n logger.info('Recording!');\n}\n\n/**\n * Sleep for specified milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n","/**\n * Audio device detection and selection\n */\n\nimport { selectAndConfigureAudioDevice } from '@theunwalked/unplayable';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport { getLogger } from './logger';\nimport type { AudioDevice } from './types';\n\n/**\n * List all available audio input devices\n * Note: This is a placeholder - @theunwalked/unplayable doesn't provide a direct API for this\n * In practice, you would use selectAndConfigureAudioDevice for device selection\n */\nexport async function listAudioDevices(): Promise<AudioDevice[]> {\n const logger = getLogger();\n\n // This would require extending @theunwalked/unplayable or using a different library\n logger.warn('listAudioDevices is not fully implemented - use selectDeviceInteractive instead');\n return [];\n}\n\n/**\n * Get the default audio input device\n */\nexport async function getDefaultDevice(): Promise<AudioDevice | null> {\n const devices = await listAudioDevices();\n return devices.find((d) => d.isDefault) || devices[0] || null;\n}\n\n/**\n * Find device by ID or name\n */\nexport async function findDevice(idOrName: string): Promise<AudioDevice | null> {\n const devices = await listAudioDevices();\n\n return (\n devices.find((d) => d.id === idOrName) ||\n devices.find((d) => d.name === idOrName) ||\n null\n );\n}\n\n/**\n * Interactive device selection using @theunwalked/unplayable\n * This function uses the built-in interactive device selector\n */\nexport async function selectDeviceInteractive(): Promise<string> {\n const logger = getLogger();\n\n try {\n const preferencesDir = join(homedir(), '.unplayable');\n const result = await selectAndConfigureAudioDevice(preferencesDir, logger, false);\n return result;\n } catch (error) {\n logger.error('Device selection failed:', error);\n throw new Error(`Device selection failed: ${error}`);\n }\n}\n\n","/**\n * Audio recording functionality\n */\n\nimport { processAudio } from '@theunwalked/unplayable';\nimport { promises as fs } from 'fs';\nimport { join } from 'path';\nimport { getLogger } from './logger';\nimport type { RecordingOptions, RecordingResult } from './types';\n\n/**\n * Record audio using @theunwalked/unplayable\n */\nexport async function recordAudio(\n options: RecordingOptions = {}\n): Promise<RecordingResult> {\n const logger = getLogger();\n\n const {\n duration,\n outputPath,\n } = options;\n\n const startTime = Date.now();\n\n try {\n logger.info('Starting audio recording...');\n logger.info('Press ENTER to stop recording');\n\n const audioResult = await processAudio({\n file: undefined,\n maxRecordingTime: duration,\n outputDirectory: outputPath ? outputPath.substring(0, outputPath.lastIndexOf('/')) : 'output',\n debug: false\n });\n\n if (audioResult.cancelled) {\n throw new Error('Recording cancelled by user');\n }\n\n const endTime = Date.now();\n const actualDuration = (endTime - startTime) / 1000;\n\n // Get file stats\n const filePath = audioResult.audioFilePath || join('output', `recording-${Date.now()}.wav`);\n const stats = await fs.stat(filePath);\n\n logger.info(`Recording complete: ${filePath}`);\n logger.info(`Duration: ${actualDuration.toFixed(2)} seconds`);\n logger.info(`File size: ${(stats.size / 1024).toFixed(2)} KB`);\n\n return {\n filePath,\n duration: actualDuration,\n fileSize: stats.size,\n };\n } catch (error) {\n logger.error('Recording failed:', error);\n throw new Error(`Recording failed: ${error}`);\n }\n}\n\n/**\n * Archive audio file to specified directory\n */\nexport async function archiveAudio(\n audioPath: string,\n archiveDir: string,\n filename?: string\n): Promise<string> {\n const logger = getLogger();\n\n try {\n // Ensure archive directory exists\n await fs.mkdir(archiveDir, { recursive: true });\n\n // Determine archive filename\n const basename = filename || `audio-${Date.now()}.wav`;\n const archivePath = join(archiveDir, basename);\n\n // Copy file to archive\n await fs.copyFile(audioPath, archivePath);\n\n logger.info(`Audio archived to: ${archivePath}`);\n return archivePath;\n } catch (error) {\n logger.error('Failed to archive audio:', error);\n throw new Error(`Archive failed: ${error}`);\n }\n}\n\n/**\n * Delete audio file\n */\nexport async function deleteAudio(audioPath: string): Promise<void> {\n const logger = getLogger();\n\n try {\n await fs.unlink(audioPath);\n logger.debug(`Deleted audio file: ${audioPath}`);\n } catch (error: any) {\n if (error.code !== 'ENOENT') {\n logger.warn(`Failed to delete audio file: ${error}`);\n }\n }\n}\n\n/**\n * Get audio file duration (requires external library or audio analysis)\n * For now, returns null - can be enhanced later\n */\nexport async function getAudioDuration(_audioPath: string): Promise<number | null> {\n // TODO: Implement audio duration detection\n // May require additional library like 'music-metadata'\n return null;\n}\n\n","/**\n * Audio transcription utilities\n * Wraps @eldrforge/ai-service for convenience\n */\n\nimport { transcribeAudio as aiTranscribe } from '@eldrforge/ai-service';\nimport { getLogger } from './logger';\n\n/**\n * Transcribe audio file using AI service\n * This is a convenience wrapper around @eldrforge/ai-service\n */\nexport async function transcribeAudio(audioPath: string): Promise<string> {\n const logger = getLogger();\n\n try {\n logger.info('Transcribing audio...');\n const result = await aiTranscribe(audioPath);\n const transcript = result.text;\n logger.info('Transcription complete');\n return transcript;\n } catch (error) {\n logger.error('Transcription failed:', error);\n throw new Error(`Transcription failed: ${error}`);\n }\n}\n\n"],"names":["logger","setLogger","newLogger","getLogger","error","message","args","console","warn","info","log","debug","countdown","seconds","onTick","i","sleep","ms","Promise","resolve","setTimeout","listAudioDevices","getDefaultDevice","devices","find","d","isDefault","findDevice","idOrName","id","name","selectDeviceInteractive","preferencesDir","join","homedir","result","selectAndConfigureAudioDevice","Error","recordAudio","options","duration","outputPath","startTime","Date","now","audioResult","processAudio","file","undefined","maxRecordingTime","outputDirectory","substring","lastIndexOf","cancelled","endTime","actualDuration","filePath","audioFilePath","stats","fs","stat","toFixed","size","fileSize","archiveAudio","audioPath","archiveDir","filename","mkdir","recursive","basename","archivePath","copyFile","deleteAudio","unlink","code","getAudioDuration","_audioPath","transcribeAudio","aiTranscribe","transcript","text"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/logger.ts","../src/countdown.ts","../src/devices.ts","../src/recording.ts","../src/transcription.ts"],"sourcesContent":["/**\n * Logger utilities for audio-tools\n */\n\nimport type { Logger } from './types';\n\nlet logger: Logger | undefined;\n\n/**\n * Set the logger instance\n */\nexport function setLogger(newLogger: Logger): void {\n logger = newLogger;\n}\n\n/**\n * Get the current logger or create a console fallback\n */\nexport function getLogger(): Logger {\n if (logger) {\n return logger;\n }\n\n // Console fallback\n /* eslint-disable no-console */\n return {\n error: (message: string, ...args: any[]) => console.error(message, ...args),\n warn: (message: string, ...args: any[]) => console.warn(message, ...args),\n info: (message: string, ...args: any[]) => console.log(message, ...args),\n debug: (message: string, ...args: any[]) => console.debug(message, ...args),\n };\n /* eslint-enable no-console */\n}\n\n","#!/usr/bin/env node\n\n/**\n * Countdown timer utility for audio recording sessions\n * Provides a visual countdown with beep warnings and color changes\n */\n\nexport interface CountdownOptions {\n /** Duration in seconds */\n durationSeconds: number;\n /** Show beep warning at 30 seconds remaining */\n beepAt30Seconds?: boolean;\n /** Change color to red at 30 seconds remaining */\n redAt30Seconds?: boolean;\n /** Callback function called every second with remaining time */\n onTick?: (remainingSeconds: number) => void;\n /** Callback function called when countdown reaches zero */\n onComplete?: () => void;\n /** Whether to clear the countdown line when finished */\n clearOnComplete?: boolean;\n}\n\n/**\n * ANSI escape codes for terminal control\n */\nconst ANSI = {\n // Cursor movement\n CURSOR_UP: '\\x1b[1A',\n CURSOR_TO_START: '\\x1b[0G',\n CLEAR_LINE: '\\x1b[2K',\n\n // Colors\n RED: '\\x1b[31m',\n GREEN: '\\x1b[32m',\n YELLOW: '\\x1b[33m',\n BLUE: '\\x1b[34m',\n MAGENTA: '\\x1b[35m',\n CYAN: '\\x1b[36m',\n WHITE: '\\x1b[37m',\n RESET: '\\x1b[0m',\n\n // Text styles\n BOLD: '\\x1b[1m',\n DIM: '\\x1b[2m'\n} as const;\n\n/**\n * Format seconds into MM:SS format\n */\nfunction formatTime(seconds: number): string {\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;\n}\n\n/**\n * Generate a beep sound using process.stdout.write with ASCII bell character\n */\nfunction beep(): void {\n process.stdout.write('\\x07'); // ASCII bell character\n}\n\n/**\n * Check if terminal supports colors and cursor movement\n */\nfunction supportsAnsi(): boolean {\n return process.stdout.isTTY &&\n process.env.TERM !== 'dumb' &&\n !process.env.NO_COLOR;\n}\n\n/**\n * Display a live countdown timer that updates in place\n */\nexport class CountdownTimer {\n private options: Required<CountdownOptions>;\n private intervalId: NodeJS.Timeout | null = null;\n private currentSeconds: number;\n private hasBeepedAt30: boolean = false;\n private isFirstDisplay: boolean = true;\n private supportsAnsi: boolean;\n private cleanupHandlers: Array<() => void> = [];\n private isDestroyed = false;\n\n constructor(options: CountdownOptions) {\n this.options = {\n beepAt30Seconds: true,\n redAt30Seconds: true,\n onTick: () => {},\n onComplete: () => {},\n clearOnComplete: false,\n ...options\n };\n this.currentSeconds = this.options.durationSeconds;\n this.supportsAnsi = supportsAnsi();\n\n // Set up cleanup handlers for process termination\n this.setupCleanupHandlers();\n }\n\n /**\n * Start the countdown timer\n */\n start(): Promise<void> {\n return new Promise((resolve) => {\n // Handle zero seconds case\n if (this.currentSeconds <= 0) {\n this.options.onComplete();\n resolve();\n return;\n }\n\n // Display initial countdown\n this.displayCountdown();\n\n this.intervalId = setInterval(() => {\n // Check if destroyed before processing\n if (this.isDestroyed) {\n clearInterval(this.intervalId!);\n resolve();\n return;\n }\n\n this.currentSeconds--;\n\n // Check for beep warning\n if (this.options.beepAt30Seconds &&\n this.currentSeconds === 30 &&\n !this.hasBeepedAt30) {\n beep();\n this.hasBeepedAt30 = true;\n }\n\n // Call tick callback\n this.options.onTick(this.currentSeconds);\n\n if (this.currentSeconds <= 0) {\n this.stop();\n this.options.onComplete();\n resolve();\n } else {\n this.displayCountdown();\n }\n }, 1000);\n });\n }\n\n /**\n * Stop the countdown timer\n */\n stop(): void {\n if (this.isDestroyed) {\n return;\n }\n\n if (this.intervalId) {\n clearInterval(this.intervalId);\n this.intervalId = null;\n }\n\n if (this.options.clearOnComplete && this.supportsAnsi) {\n // Clear the countdown line\n process.stdout.write(ANSI.CURSOR_TO_START + ANSI.CLEAR_LINE);\n } else if (!this.isFirstDisplay) {\n // Add a newline if we've been updating in place\n process.stdout.write('\\n');\n }\n }\n\n /**\n * Get current remaining time\n */\n getRemainingSeconds(): number {\n return this.currentSeconds;\n }\n\n /**\n * Display the countdown timer\n */\n private displayCountdown(): void {\n const timeString = formatTime(this.currentSeconds);\n const isWarningTime = this.currentSeconds <= 30;\n\n let output: string;\n\n if (this.supportsAnsi) {\n // Use colors and in-place updating if supported\n if (!this.isFirstDisplay) {\n // Move cursor up and clear the line to overwrite previous countdown\n process.stdout.write(ANSI.CURSOR_UP + ANSI.CURSOR_TO_START + ANSI.CLEAR_LINE);\n }\n\n const color = isWarningTime && this.options.redAt30Seconds ? ANSI.RED : ANSI.CYAN;\n const style = isWarningTime ? ANSI.BOLD : '';\n\n output = `${color}${style}⏱️ Recording time remaining: ${timeString}${ANSI.RESET}`;\n } else {\n // Fallback for terminals that don't support ANSI\n const warning = isWarningTime ? ' ⚠️ ' : '';\n output = `⏱️ Recording time remaining: ${timeString}${warning}`;\n }\n\n process.stdout.write(output + '\\n');\n this.isFirstDisplay = false;\n }\n\n /**\n * Set up cleanup handlers for process termination and uncaught exceptions\n */\n private setupCleanupHandlers(): void {\n // Skip setting up process listeners in test environments to avoid listener leaks\n if (process.env.NODE_ENV === 'test' || process.env.VITEST) {\n return;\n }\n\n const cleanup = () => {\n this.destroy();\n };\n\n // Handle various exit scenarios\n const exitHandler = () => cleanup();\n const uncaughtExceptionHandler = (error: Error) => {\n cleanup();\n // Re-throw to maintain normal error handling\n throw error;\n };\n\n process.on('exit', exitHandler);\n process.on('SIGINT', exitHandler);\n process.on('SIGTERM', exitHandler);\n process.on('uncaughtException', uncaughtExceptionHandler);\n process.on('unhandledRejection', cleanup);\n\n // Store handlers for removal during destroy\n this.cleanupHandlers = [\n () => process.removeListener('exit', exitHandler),\n () => process.removeListener('SIGINT', exitHandler),\n () => process.removeListener('SIGTERM', exitHandler),\n () => process.removeListener('uncaughtException', uncaughtExceptionHandler),\n () => process.removeListener('unhandledRejection', cleanup)\n ];\n }\n\n /**\n * Destroy the timer and clean up all resources\n */\n destroy(): void {\n if (this.isDestroyed) {\n return;\n }\n\n this.isDestroyed = true;\n this.stop();\n\n // Remove all process event listeners\n this.cleanupHandlers.forEach(handler => {\n try {\n handler();\n } catch {\n // Ignore errors during cleanup\n }\n });\n this.cleanupHandlers = [];\n }\n\n /**\n * Check if the timer has been destroyed\n */\n isTimerDestroyed(): boolean {\n return this.isDestroyed;\n }\n}\n\n/**\n * Create and start a countdown timer (convenience function)\n */\nexport async function startCountdown(options: CountdownOptions): Promise<void> {\n const timer = new CountdownTimer(options);\n return timer.start();\n}\n\n/**\n * Create a countdown timer for audio recording with sensible defaults\n */\nexport function createAudioRecordingCountdown(durationSeconds: number): CountdownTimer {\n return new CountdownTimer({\n durationSeconds,\n beepAt30Seconds: true,\n redAt30Seconds: true,\n clearOnComplete: true\n });\n}\n\n/**\n * Simple countdown function for backwards compatibility\n * @param seconds Number of seconds to count down\n * @param onTick Optional callback called on each tick\n * @deprecated Use CountdownTimer or startCountdown for more features\n */\nexport async function countdown(\n seconds: number,\n onTick?: (remaining: number) => void\n): Promise<void> {\n const options: CountdownOptions = {\n durationSeconds: seconds,\n beepAt30Seconds: false,\n redAt30Seconds: false,\n clearOnComplete: false\n };\n\n if (onTick) {\n options.onTick = onTick;\n }\n\n await startCountdown(options);\n}\n","/**\n * Audio device detection and selection\n */\n\nimport { selectAndConfigureAudioDevice } from '@theunwalked/unplayable';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport { getLogger } from './logger';\nimport type { AudioDevice } from './types';\n\n/**\n * List all available audio input devices\n * Note: This is a placeholder - @theunwalked/unplayable doesn't provide a direct API for this\n * In practice, you would use selectAndConfigureAudioDevice for device selection\n */\nexport async function listAudioDevices(): Promise<AudioDevice[]> {\n const logger = getLogger();\n\n // This would require extending @theunwalked/unplayable or using a different library\n logger.warn('listAudioDevices is not fully implemented - use selectDeviceInteractive instead');\n return [];\n}\n\n/**\n * Get the default audio input device\n */\nexport async function getDefaultDevice(): Promise<AudioDevice | null> {\n const devices = await listAudioDevices();\n return devices.find((d) => d.isDefault) || devices[0] || null;\n}\n\n/**\n * Find device by ID or name\n */\nexport async function findDevice(idOrName: string): Promise<AudioDevice | null> {\n const devices = await listAudioDevices();\n\n return (\n devices.find((d) => d.id === idOrName) ||\n devices.find((d) => d.name === idOrName) ||\n null\n );\n}\n\n/**\n * Interactive device selection using @theunwalked/unplayable\n * This function uses the built-in interactive device selector\n */\nexport async function selectDeviceInteractive(): Promise<string> {\n const logger = getLogger();\n\n try {\n const preferencesDir = join(homedir(), '.unplayable');\n const result = await selectAndConfigureAudioDevice(preferencesDir, logger, false);\n return result;\n } catch (error) {\n logger.error('Device selection failed:', error);\n throw new Error(`Device selection failed: ${error}`);\n }\n}\n\n","/**\n * Audio recording functionality\n */\n\nimport { processAudio } from '@theunwalked/unplayable';\nimport { promises as fs } from 'fs';\nimport { join } from 'path';\nimport { getLogger } from './logger';\nimport type { RecordingOptions, RecordingResult } from './types';\n\n/**\n * Record audio using @theunwalked/unplayable\n */\nexport async function recordAudio(\n options: RecordingOptions = {}\n): Promise<RecordingResult> {\n const logger = getLogger();\n\n const {\n duration,\n outputPath,\n } = options;\n\n const startTime = Date.now();\n\n try {\n logger.info('Starting audio recording...');\n logger.info('Press ENTER to stop recording');\n\n const audioResult = await processAudio({\n file: undefined,\n maxRecordingTime: duration,\n outputDirectory: outputPath ? outputPath.substring(0, outputPath.lastIndexOf('/')) : 'output',\n debug: false\n });\n\n if (audioResult.cancelled) {\n throw new Error('Recording cancelled by user');\n }\n\n const endTime = Date.now();\n const actualDuration = (endTime - startTime) / 1000;\n\n // Get file stats\n const filePath = audioResult.audioFilePath || join('output', `recording-${Date.now()}.wav`);\n const stats = await fs.stat(filePath);\n\n logger.info(`Recording complete: ${filePath}`);\n logger.info(`Duration: ${actualDuration.toFixed(2)} seconds`);\n logger.info(`File size: ${(stats.size / 1024).toFixed(2)} KB`);\n\n return {\n filePath,\n duration: actualDuration,\n fileSize: stats.size,\n };\n } catch (error) {\n logger.error('Recording failed:', error);\n throw new Error(`Recording failed: ${error}`);\n }\n}\n\n/**\n * Get timestamped filename for archiving\n */\nfunction getTimestampedFilename(baseName: string, extension: string = '.json'): string {\n const now = new Date();\n\n // Format as YYMMdd-HHmm (e.g., 250701-1030)\n const yy = now.getFullYear().toString().slice(-2);\n const mm = (now.getMonth() + 1).toString().padStart(2, '0');\n const dd = now.getDate().toString().padStart(2, '0');\n const hh = now.getHours().toString().padStart(2, '0');\n const min = now.getMinutes().toString().padStart(2, '0');\n\n const timestamp = `${yy}${mm}${dd}-${hh}${min}`;\n\n return `${timestamp}-${baseName}${extension}`;\n}\n\n/**\n * Get timestamped filename for archived audio\n */\nexport function getTimestampedArchivedAudioFilename(originalExtension: string = '.wav'): string {\n return getTimestampedFilename('review-audio', originalExtension);\n}\n\n/**\n * Get timestamped filename for archived transcript\n */\nexport function getTimestampedArchivedTranscriptFilename(): string {\n return getTimestampedFilename('review-transcript', '.md');\n}\n\n/**\n * Archive audio file with transcription to specified directory\n * This saves BOTH the audio file AND transcription text together\n * @param originalAudioPath Path to the original audio file\n * @param transcriptionText The transcribed text content\n * @param outputDirectory Directory to save archived files (default: 'output')\n * @returns Paths to both archived audio and transcript files\n */\nexport async function archiveAudio(\n originalAudioPath: string,\n transcriptionText: string,\n outputDirectory: string = 'output'\n): Promise<{ audioPath: string; transcriptPath: string }> {\n const logger = getLogger();\n const path = await import('path');\n\n try {\n // Ensure the output directory exists\n await fs.mkdir(outputDirectory, { recursive: true });\n\n // Get file extension from original audio file\n const originalExtension = path.extname(originalAudioPath);\n\n // Generate timestamped filenames\n const archivedAudioFilename = getTimestampedArchivedAudioFilename(originalExtension);\n const archivedTranscriptFilename = getTimestampedArchivedTranscriptFilename();\n\n // Full paths for archived files\n const archivedAudioPath = path.join(outputDirectory, archivedAudioFilename);\n const archivedTranscriptPath = path.join(outputDirectory, archivedTranscriptFilename);\n\n // Copy audio file if it exists\n try {\n await fs.access(originalAudioPath);\n const audioBuffer = await fs.readFile(originalAudioPath);\n await fs.writeFile(archivedAudioPath, audioBuffer);\n logger.debug('Archived audio file to: %s', archivedAudioPath);\n } catch {\n logger.warn('AUDIO_FILE_NOT_FOUND: Original audio file not accessible | Path: %s | Impact: Cannot archive original', originalAudioPath);\n }\n\n // Save transcription text\n const transcriptContent = `# Audio Transcription Archive\\n\\n**Original Audio File:** ${originalAudioPath}\\n**Archived:** ${new Date().toISOString()}\\n\\n## Transcription\\n\\n${transcriptionText}`;\n await fs.writeFile(archivedTranscriptPath, transcriptContent, 'utf8');\n logger.debug('Archived transcription to: %s', archivedTranscriptPath);\n\n logger.info('AUDIO_ARCHIVED: Audio and transcript archived successfully | Audio: %s | Transcript: %s | Status: archived', archivedAudioFilename, archivedTranscriptFilename);\n\n return {\n audioPath: archivedAudioPath,\n transcriptPath: archivedTranscriptPath\n };\n\n } catch (error: any) {\n logger.error('AUDIO_ARCHIVE_FAILED: Failed to archive audio files | Error: %s | Impact: Audio not preserved', error.message);\n throw new Error(`Audio archiving failed: ${error.message}`);\n }\n}\n\n/**\n * Delete audio file\n */\nexport async function deleteAudio(audioPath: string): Promise<void> {\n const logger = getLogger();\n\n try {\n await fs.unlink(audioPath);\n logger.debug(`Deleted audio file: ${audioPath}`);\n } catch (error: any) {\n if (error.code !== 'ENOENT') {\n logger.warn(`Failed to delete audio file: ${error}`);\n }\n }\n}\n\n/**\n * Get audio file duration (requires external library or audio analysis)\n * For now, returns null - can be enhanced later\n */\nexport async function getAudioDuration(_audioPath: string): Promise<number | null> {\n // TODO: Implement audio duration detection\n // May require additional library like 'music-metadata'\n return null;\n}\n\n","/**\n * Audio transcription utilities\n * Wraps @eldrforge/ai-service for convenience\n */\n\nimport { transcribeAudio as aiTranscribe } from '@eldrforge/ai-service';\nimport { getLogger } from './logger';\n\n/**\n * Transcribe audio file using AI service\n * This is a convenience wrapper around @eldrforge/ai-service\n */\nexport async function transcribeAudio(audioPath: string): Promise<string> {\n const logger = getLogger();\n\n try {\n logger.info('Transcribing audio...');\n const result = await aiTranscribe(audioPath);\n const transcript = result.text;\n logger.info('Transcription complete');\n return transcript;\n } catch (error) {\n logger.error('Transcription failed:', error);\n throw new Error(`Transcription failed: ${error}`);\n }\n}\n\n"],"names":["logger","setLogger","newLogger","getLogger","error","message","args","console","warn","info","log","debug","ANSI","CURSOR_UP","CURSOR_TO_START","CLEAR_LINE","RED","CYAN","RESET","BOLD","formatTime","seconds","minutes","Math","floor","remainingSeconds","toString","padStart","beep","process","stdout","write","supportsAnsi","isTTY","env","TERM","NO_COLOR","CountdownTimer","start","Promise","resolve","currentSeconds","options","onComplete","displayCountdown","intervalId","setInterval","isDestroyed","clearInterval","beepAt30Seconds","hasBeepedAt30","onTick","stop","clearOnComplete","isFirstDisplay","getRemainingSeconds","timeString","isWarningTime","output","color","redAt30Seconds","style","warning","setupCleanupHandlers","NODE_ENV","VITEST","cleanup","destroy","exitHandler","uncaughtExceptionHandler","on","cleanupHandlers","removeListener","forEach","handler","isTimerDestroyed","durationSeconds","startCountdown","timer","createAudioRecordingCountdown","countdown","listAudioDevices","getDefaultDevice","devices","find","d","isDefault","findDevice","idOrName","id","name","selectDeviceInteractive","preferencesDir","join","homedir","result","selectAndConfigureAudioDevice","Error","recordAudio","duration","outputPath","startTime","Date","now","audioResult","processAudio","file","undefined","maxRecordingTime","outputDirectory","substring","lastIndexOf","cancelled","endTime","actualDuration","filePath","audioFilePath","stats","fs","stat","toFixed","size","fileSize","getTimestampedFilename","baseName","extension","yy","getFullYear","slice","mm","getMonth","dd","getDate","hh","getHours","min","getMinutes","timestamp","getTimestampedArchivedAudioFilename","originalExtension","getTimestampedArchivedTranscriptFilename","archiveAudio","originalAudioPath","transcriptionText","path","mkdir","recursive","extname","archivedAudioFilename","archivedTranscriptFilename","archivedAudioPath","archivedTranscriptPath","access","audioBuffer","readFile","writeFile","transcriptContent","toISOString","audioPath","transcriptPath","deleteAudio","unlink","code","getAudioDuration","_audioPath","transcribeAudio","aiTranscribe","transcript","text"],"mappings":"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"}
@@ -1,10 +1,77 @@
1
+ #!/usr/bin/env node
1
2
  /**
2
- * Countdown timer for audio recording
3
+ * Countdown timer utility for audio recording sessions
4
+ * Provides a visual countdown with beep warnings and color changes
3
5
  */
6
+ export interface CountdownOptions {
7
+ /** Duration in seconds */
8
+ durationSeconds: number;
9
+ /** Show beep warning at 30 seconds remaining */
10
+ beepAt30Seconds?: boolean;
11
+ /** Change color to red at 30 seconds remaining */
12
+ redAt30Seconds?: boolean;
13
+ /** Callback function called every second with remaining time */
14
+ onTick?: (remainingSeconds: number) => void;
15
+ /** Callback function called when countdown reaches zero */
16
+ onComplete?: () => void;
17
+ /** Whether to clear the countdown line when finished */
18
+ clearOnComplete?: boolean;
19
+ }
4
20
  /**
5
- * Display countdown before recording
21
+ * Display a live countdown timer that updates in place
22
+ */
23
+ export declare class CountdownTimer {
24
+ private options;
25
+ private intervalId;
26
+ private currentSeconds;
27
+ private hasBeepedAt30;
28
+ private isFirstDisplay;
29
+ private supportsAnsi;
30
+ private cleanupHandlers;
31
+ private isDestroyed;
32
+ constructor(options: CountdownOptions);
33
+ /**
34
+ * Start the countdown timer
35
+ */
36
+ start(): Promise<void>;
37
+ /**
38
+ * Stop the countdown timer
39
+ */
40
+ stop(): void;
41
+ /**
42
+ * Get current remaining time
43
+ */
44
+ getRemainingSeconds(): number;
45
+ /**
46
+ * Display the countdown timer
47
+ */
48
+ private displayCountdown;
49
+ /**
50
+ * Set up cleanup handlers for process termination and uncaught exceptions
51
+ */
52
+ private setupCleanupHandlers;
53
+ /**
54
+ * Destroy the timer and clean up all resources
55
+ */
56
+ destroy(): void;
57
+ /**
58
+ * Check if the timer has been destroyed
59
+ */
60
+ isTimerDestroyed(): boolean;
61
+ }
62
+ /**
63
+ * Create and start a countdown timer (convenience function)
64
+ */
65
+ export declare function startCountdown(options: CountdownOptions): Promise<void>;
66
+ /**
67
+ * Create a countdown timer for audio recording with sensible defaults
68
+ */
69
+ export declare function createAudioRecordingCountdown(durationSeconds: number): CountdownTimer;
70
+ /**
71
+ * Simple countdown function for backwards compatibility
6
72
  * @param seconds Number of seconds to count down
7
73
  * @param onTick Optional callback called on each tick
74
+ * @deprecated Use CountdownTimer or startCountdown for more features
8
75
  */
9
76
  export declare function countdown(seconds: number, onTick?: (remaining: number) => void): Promise<void>;
10
77
  //# sourceMappingURL=countdown.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"countdown.d.ts","sourceRoot":"","sources":["../../src/countdown.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;;GAIG;AACH,wBAAsB,SAAS,CAC3B,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,GACrC,OAAO,CAAC,IAAI,CAAC,CAcf"}
1
+ {"version":3,"file":"countdown.d.ts","sourceRoot":"","sources":["../../src/countdown.ts"],"names":[],"mappings":";AAEA;;;GAGG;AAEH,MAAM,WAAW,gBAAgB;IAC7B,0BAA0B;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kDAAkD;IAClD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,gEAAgE;IAChE,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,wDAAwD;IACxD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAmDD;;GAEG;AACH,qBAAa,cAAc;IACvB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,gBAAgB;IAgBrC;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA4CtB;;OAEG;IACH,IAAI,IAAI,IAAI;IAmBZ;;OAEG;IACH,mBAAmB,IAAI,MAAM;IAI7B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA2BxB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAkC5B;;OAEG;IACH,OAAO,IAAI,IAAI;IAmBf;;OAEG;IACH,gBAAgB,IAAI,OAAO;CAG9B;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG7E;AAED;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,eAAe,EAAE,MAAM,GAAG,cAAc,CAOrF;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAC3B,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,GACrC,OAAO,CAAC,IAAI,CAAC,CAaf"}
@@ -4,8 +4,9 @@
4
4
  */
5
5
  export * from './types';
6
6
  export { setLogger, getLogger } from './logger';
7
- export { countdown } from './countdown';
7
+ export { countdown, CountdownTimer, startCountdown, createAudioRecordingCountdown } from './countdown';
8
+ export type { CountdownOptions } from './countdown';
8
9
  export { listAudioDevices, getDefaultDevice, findDevice, selectDeviceInteractive, } from './devices';
9
- export { recordAudio, archiveAudio, deleteAudio, getAudioDuration, } from './recording';
10
+ export { recordAudio, archiveAudio, deleteAudio, getAudioDuration, getTimestampedArchivedAudioFilename, getTimestampedArchivedTranscriptFilename, } from './recording';
10
11
  export { transcribeAudio } from './transcription';
11
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAGhD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,OAAO,EACH,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,uBAAuB,GAC1B,MAAM,WAAW,CAAC;AAGnB,OAAO,EACH,WAAW,EACX,YAAY,EACZ,WAAW,EACX,gBAAgB,GACnB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAGhD,OAAO,EACH,SAAS,EACT,cAAc,EACd,cAAc,EACd,6BAA6B,EAChC,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,OAAO,EACH,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,uBAAuB,GAC1B,MAAM,WAAW,CAAC;AAGnB,OAAO,EACH,WAAW,EACX,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,mCAAmC,EACnC,wCAAwC,GAC3C,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
@@ -4,9 +4,25 @@ import { RecordingOptions, RecordingResult } from './types';
4
4
  */
5
5
  export declare function recordAudio(options?: RecordingOptions): Promise<RecordingResult>;
6
6
  /**
7
- * Archive audio file to specified directory
7
+ * Get timestamped filename for archived audio
8
8
  */
9
- export declare function archiveAudio(audioPath: string, archiveDir: string, filename?: string): Promise<string>;
9
+ export declare function getTimestampedArchivedAudioFilename(originalExtension?: string): string;
10
+ /**
11
+ * Get timestamped filename for archived transcript
12
+ */
13
+ export declare function getTimestampedArchivedTranscriptFilename(): string;
14
+ /**
15
+ * Archive audio file with transcription to specified directory
16
+ * This saves BOTH the audio file AND transcription text together
17
+ * @param originalAudioPath Path to the original audio file
18
+ * @param transcriptionText The transcribed text content
19
+ * @param outputDirectory Directory to save archived files (default: 'output')
20
+ * @returns Paths to both archived audio and transcript files
21
+ */
22
+ export declare function archiveAudio(originalAudioPath: string, transcriptionText: string, outputDirectory?: string): Promise<{
23
+ audioPath: string;
24
+ transcriptPath: string;
25
+ }>;
10
26
  /**
11
27
  * Delete audio file
12
28
  */
@@ -1 +1 @@
1
- {"version":3,"file":"recording.d.ts","sourceRoot":"","sources":["../../src/recording.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAEjE;;GAEG;AACH,wBAAsB,WAAW,CAC7B,OAAO,GAAE,gBAAqB,GAC/B,OAAO,CAAC,eAAe,CAAC,CA6C1B;AAED;;GAEG;AACH,wBAAsB,YAAY,CAC9B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,CAoBjB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWlE;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIjF"}
1
+ {"version":3,"file":"recording.d.ts","sourceRoot":"","sources":["../../src/recording.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAEjE;;GAEG;AACH,wBAAsB,WAAW,CAC7B,OAAO,GAAE,gBAAqB,GAC/B,OAAO,CAAC,eAAe,CAAC,CA6C1B;AAoBD;;GAEG;AACH,wBAAgB,mCAAmC,CAAC,iBAAiB,GAAE,MAAe,GAAG,MAAM,CAE9F;AAED;;GAEG;AACH,wBAAgB,wCAAwC,IAAI,MAAM,CAEjE;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAC9B,iBAAiB,EAAE,MAAM,EACzB,iBAAiB,EAAE,MAAM,EACzB,eAAe,GAAE,MAAiB,GACnC,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC,CA6CxD;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWlE;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIjF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eldrforge/audio-tools",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Audio recording tools for voice-driven development workflows",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",