@redaksjon/protokoll 0.0.6

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 (82) hide show
  1. package/.nvmrc +2 -0
  2. package/LICENSE +190 -0
  3. package/README.md +88 -0
  4. package/dist/arguments.js +158 -0
  5. package/dist/arguments.js.map +1 -0
  6. package/dist/constants.js +82 -0
  7. package/dist/constants.js.map +1 -0
  8. package/dist/logging.js +46 -0
  9. package/dist/logging.js.map +1 -0
  10. package/dist/main.js +5 -0
  11. package/dist/main.js.map +1 -0
  12. package/dist/phases/locate.js +55 -0
  13. package/dist/phases/locate.js.map +1 -0
  14. package/dist/phases/transcribe.js +149 -0
  15. package/dist/phases/transcribe.js.map +1 -0
  16. package/dist/processor.js +35 -0
  17. package/dist/processor.js.map +1 -0
  18. package/dist/prompt/instructions/transcribe.md +46 -0
  19. package/dist/prompt/personas/transcriber.md +37 -0
  20. package/dist/prompt/transcribe.js +41 -0
  21. package/dist/prompt/transcribe.js.map +1 -0
  22. package/dist/protokoll.js +81 -0
  23. package/dist/protokoll.js.map +1 -0
  24. package/dist/util/dates.js +96 -0
  25. package/dist/util/dates.js.map +1 -0
  26. package/dist/util/general.js +39 -0
  27. package/dist/util/general.js.map +1 -0
  28. package/dist/util/media.js +103 -0
  29. package/dist/util/media.js.map +1 -0
  30. package/dist/util/openai.js +92 -0
  31. package/dist/util/openai.js.map +1 -0
  32. package/dist/util/storage.js +135 -0
  33. package/dist/util/storage.js.map +1 -0
  34. package/docs/index.html +16 -0
  35. package/docs/package-lock.json +1521 -0
  36. package/docs/package.json +21 -0
  37. package/docs/vite.config.js +10 -0
  38. package/eslint.config.mjs +82 -0
  39. package/nodemon.json +14 -0
  40. package/output/kodrdriv/250702-1905-commit-message.md +1 -0
  41. package/output/kodrdriv/250702-1905-commit.request.json +14 -0
  42. package/output/kodrdriv/250702-1905-commit.response.json +36 -0
  43. package/output/kodrdriv/250702-1906-commit-message.md +1 -0
  44. package/output/kodrdriv/250702-1907-commit-message.md +1 -0
  45. package/output/kodrdriv/250702-1907-commit.request.json +14 -0
  46. package/output/kodrdriv/250702-1907-commit.response.json +36 -0
  47. package/output/kodrdriv/250716-1517-review-analysis.md +39 -0
  48. package/output/kodrdriv/250716-1517-review-notes.md +69 -0
  49. package/output/kodrdriv/250716-1518-review-analysis.md +15 -0
  50. package/output/kodrdriv/250716-1518-review-notes.md +67 -0
  51. package/output/kodrdriv/250716-1523-review-analysis.md +36 -0
  52. package/output/kodrdriv/250716-1523-review-notes.md +87 -0
  53. package/output/kodrdriv/250722-1135-commit-message.md +1 -0
  54. package/output/kodrdriv/250722-1331-commit-message.md +1 -0
  55. package/output/kodrdriv/250722-1335-commit-message.md +1 -0
  56. package/output/kodrdriv/250722-1337-commit-message.md +1 -0
  57. package/output/kodrdriv/250722-1342-release-notes.md +26 -0
  58. package/output/kodrdriv/250722-1416-commit-message.md +3 -0
  59. package/output/kodrdriv/250722-1420-commit-message.md +1 -0
  60. package/output/kodrdriv/250722-1422-commit-message.md +1 -0
  61. package/output/kodrdriv/250722-1423-commit-message.md +1 -0
  62. package/output/kodrdriv/250722-1425-release-notes.md +41 -0
  63. package/output/kodrdriv/250722-1527-commit-message.md +13 -0
  64. package/output/kodrdriv/250722-1532-commit-message.md +1 -0
  65. package/output/kodrdriv/250722-1532-release-notes.md +32 -0
  66. package/output/kodrdriv/250722-2314-review-analysis.md +28 -0
  67. package/output/kodrdriv/250722-2314-review-notes.md +464 -0
  68. package/output/kodrdriv/250722-2315-review-analysis.md +28 -0
  69. package/output/kodrdriv/250722-2315-review-notes.md +477 -0
  70. package/output/kodrdriv/250804-1623-review-analysis.md +38 -0
  71. package/output/kodrdriv/250804-1623-review-notes.md +479 -0
  72. package/output/kodrdriv/250804-1638-review-analysis.md +56 -0
  73. package/output/kodrdriv/250804-1638-review-notes.md +502 -0
  74. package/output/kodrdriv/250812-2021-review-analysis.md +27 -0
  75. package/output/kodrdriv/250812-2021-review-notes.md +571 -0
  76. package/output/kodrdriv/250826-0700-commit-message.md +12 -0
  77. package/output/kodrdriv/RELEASE_NOTES.md +30 -0
  78. package/output/kodrdriv/RELEASE_TITLE.md +1 -0
  79. package/package.json +78 -0
  80. package/tsconfig.tsbuildinfo +1 -0
  81. package/vite.config.ts +124 -0
  82. package/vitest.config.ts +30 -0
@@ -0,0 +1,55 @@
1
+ import { getLogger } from '../logging.js';
2
+ import { create as create$3 } from '../util/media.js';
3
+ import { create as create$1 } from '../util/storage.js';
4
+ import { create as create$2 } from '../util/dates.js';
5
+ import path from 'path';
6
+
7
+ const create = (config, operator)=>{
8
+ const logger = getLogger();
9
+ const storage = create$1({
10
+ log: logger.debug
11
+ });
12
+ const dates = create$2({
13
+ timezone: config.timezone
14
+ });
15
+ const media = create$3(logger);
16
+ const locate = async (audioFile)=>{
17
+ logger.debug('Processing file %s', audioFile);
18
+ // Extract audio file creation time
19
+ let creationTime = await media.getAudioCreationTime(audioFile);
20
+ try {
21
+ if (creationTime) {
22
+ logger.info('Audio recording time: %s', creationTime.toISOString());
23
+ } else {
24
+ logger.warn('Could not determine audio recording time for %s, using current date', audioFile);
25
+ creationTime = dates.now();
26
+ }
27
+ } catch (error) {
28
+ logger.error('Error determining audio recording time for %s: %s, using current date', audioFile, error.message);
29
+ creationTime = dates.now();
30
+ }
31
+ // Calculate the hash of file and output directory
32
+ const hash = (await storage.hashFile(audioFile, 100)).substring(0, 8);
33
+ const outputPath = await operator.constructOutputDirectory(creationTime);
34
+ const contextPath = path.join(outputPath, '.context');
35
+ await storage.createDirectory(contextPath);
36
+ const interimPath = path.join(outputPath, '.interim');
37
+ await storage.createDirectory(interimPath);
38
+ const transcriptionFilename = await operator.constructFilename(creationTime, 'transcription', hash);
39
+ return {
40
+ creationTime,
41
+ outputPath,
42
+ contextPath,
43
+ interimPath,
44
+ transcriptionFilename,
45
+ hash,
46
+ audioFile
47
+ };
48
+ };
49
+ return {
50
+ locate
51
+ };
52
+ };
53
+
54
+ export { create };
55
+ //# sourceMappingURL=locate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locate.js","sources":["../../src/phases/locate.ts"],"sourcesContent":["import * as Logging from '@/logging';\nimport * as Media from '@/util/media';\nimport * as Storage from '@/util/storage';\nimport * as Dreadcabinet from '@theunwalked/dreadcabinet';\nimport * as Dates from '@/util/dates';\nimport { Config } from '@/protokoll';\nimport path from 'path';\n\n// Helper function to promisify ffmpeg.\n\nexport interface Instance {\n locate: (audioFile: string) => Promise<{\n creationTime: Date;\n outputPath: string;\n contextPath: string;\n interimPath: string;\n transcriptionFilename: string;\n hash: string;\n audioFile: string;\n }>;\n}\n\nexport const create = (config: Config, operator: Dreadcabinet.Operator): Instance => {\n const logger = Logging.getLogger();\n const storage = Storage.create({ log: logger.debug });\n const dates = Dates.create({ timezone: config.timezone });\n const media = Media.create(logger);\n\n const locate = async (audioFile: string): Promise<{\n creationTime: Date;\n outputPath: string;\n contextPath: string;\n interimPath: string;\n transcriptionFilename: string;\n hash: string;\n audioFile: string;\n }> => {\n logger.debug('Processing file %s', audioFile);\n\n // Extract audio file creation time\n let creationTime = await media.getAudioCreationTime(audioFile);\n try {\n if (creationTime) {\n logger.info('Audio recording time: %s', creationTime.toISOString());\n } else {\n logger.warn('Could not determine audio recording time for %s, using current date', audioFile);\n creationTime = dates.now();\n }\n } catch (error: any) {\n logger.error('Error determining audio recording time for %s: %s, using current date', audioFile, error.message);\n creationTime = dates.now();\n }\n\n // Calculate the hash of file and output directory\n const hash = (await storage.hashFile(audioFile, 100)).substring(0, 8);\n const outputPath: string = await operator.constructOutputDirectory(creationTime);\n const contextPath: string = path.join(outputPath, '.context');\n await storage.createDirectory(contextPath);\n const interimPath: string = path.join(outputPath, '.interim');\n await storage.createDirectory(interimPath);\n const transcriptionFilename = await operator.constructFilename(creationTime, 'transcription', hash);\n\n return {\n creationTime,\n outputPath,\n contextPath,\n interimPath,\n transcriptionFilename,\n hash,\n audioFile,\n };\n }\n\n return {\n locate,\n }\n}\n\n\n"],"names":["create","config","operator","logger","Logging","storage","Storage","log","debug","dates","Dates","timezone","media","Media","locate","audioFile","creationTime","getAudioCreationTime","info","toISOString","warn","now","error","message","hash","hashFile","substring","outputPath","constructOutputDirectory","contextPath","path","join","createDirectory","interimPath","transcriptionFilename","constructFilename"],"mappings":";;;;;;AAsBO,MAAMA,MAAAA,GAAS,CAACC,MAAAA,EAAgBC,QAAAA,GAAAA;IACnC,MAAMC,MAAAA,GAASC,SAAiB,EAAA;IAChC,MAAMC,OAAAA,GAAUC,QAAc,CAAC;AAAEC,QAAAA,GAAAA,EAAKJ,OAAOK;AAAM,KAAA,CAAA;IACnD,MAAMC,KAAAA,GAAQC,QAAY,CAAC;AAAEC,QAAAA,QAAAA,EAAUV,OAAOU;AAAS,KAAA,CAAA;IACvD,MAAMC,KAAAA,GAAQC,QAAY,CAACV,MAAAA,CAAAA;AAE3B,IAAA,MAAMW,SAAS,OAAOC,SAAAA,GAAAA;QASlBZ,MAAAA,CAAOK,KAAK,CAAC,oBAAA,EAAsBO,SAAAA,CAAAA;;AAGnC,QAAA,IAAIC,YAAAA,GAAe,MAAMJ,KAAAA,CAAMK,oBAAoB,CAACF,SAAAA,CAAAA;QACpD,IAAI;AACA,YAAA,IAAIC,YAAAA,EAAc;AACdb,gBAAAA,MAAAA,CAAOe,IAAI,CAAC,0BAAA,EAA4BF,YAAAA,CAAaG,WAAW,EAAA,CAAA;YACpE,CAAA,MAAO;gBACHhB,MAAAA,CAAOiB,IAAI,CAAC,qEAAA,EAAuEL,SAAAA,CAAAA;AACnFC,gBAAAA,YAAAA,GAAeP,MAAMY,GAAG,EAAA;AAC5B,YAAA;AACJ,QAAA,CAAA,CAAE,OAAOC,KAAAA,EAAY;AACjBnB,YAAAA,MAAAA,CAAOmB,KAAK,CAAC,uEAAA,EAAyEP,SAAAA,EAAWO,MAAMC,OAAO,CAAA;AAC9GP,YAAAA,YAAAA,GAAeP,MAAMY,GAAG,EAAA;AAC5B,QAAA;;AAGA,QAAA,MAAMG,IAAAA,GAAQ,CAAA,MAAMnB,OAAAA,CAAQoB,QAAQ,CAACV,SAAAA,EAAW,GAAA,CAAG,EAAGW,SAAS,CAAC,CAAA,EAAG,CAAA,CAAA;AACnE,QAAA,MAAMC,UAAAA,GAAqB,MAAMzB,QAAAA,CAAS0B,wBAAwB,CAACZ,YAAAA,CAAAA;AACnE,QAAA,MAAMa,WAAAA,GAAsBC,IAAAA,CAAKC,IAAI,CAACJ,UAAAA,EAAY,UAAA,CAAA;QAClD,MAAMtB,OAAAA,CAAQ2B,eAAe,CAACH,WAAAA,CAAAA;AAC9B,QAAA,MAAMI,WAAAA,GAAsBH,IAAAA,CAAKC,IAAI,CAACJ,UAAAA,EAAY,UAAA,CAAA;QAClD,MAAMtB,OAAAA,CAAQ2B,eAAe,CAACC,WAAAA,CAAAA;AAC9B,QAAA,MAAMC,wBAAwB,MAAMhC,QAAAA,CAASiC,iBAAiB,CAACnB,cAAc,eAAA,EAAiBQ,IAAAA,CAAAA;QAE9F,OAAO;AACHR,YAAAA,YAAAA;AACAW,YAAAA,UAAAA;AACAE,YAAAA,WAAAA;AACAI,YAAAA,WAAAA;AACAC,YAAAA,qBAAAA;AACAV,YAAAA,IAAAA;AACAT,YAAAA;AACJ,SAAA;AACJ,IAAA,CAAA;IAEA,OAAO;AACHD,QAAAA;AACJ,KAAA;AACJ;;;;"}
@@ -0,0 +1,149 @@
1
+ import { getLogger } from '../logging.js';
2
+ import { create as create$1 } from '../util/storage.js';
3
+ import { create as create$2 } from '../util/media.js';
4
+ import { transcribeAudio, createCompletion } from '../util/openai.js';
5
+ import { stringifyJSON } from '../util/general.js';
6
+ import path from 'path';
7
+ import { Formatter } from '@riotprompt/riotprompt';
8
+ import { create as create$3 } from '../prompt/transcribe.js';
9
+
10
+ const create = (config, operator)=>{
11
+ const logger = getLogger();
12
+ const storage = create$1({
13
+ log: logger.debug
14
+ });
15
+ const media = create$2(logger);
16
+ const prompts = create$3(config.model, config);
17
+ const transcribe = async (creation, outputPath, contextPath, interimPath, filename, hash, audioFile)=>{
18
+ if (!outputPath) {
19
+ throw new Error("outputPath is required for transcribe function");
20
+ }
21
+ if (!audioFile) {
22
+ throw new Error("audioFile is required for transcribe function");
23
+ }
24
+ // Remove extension from audioFile and make the name filesafe
25
+ const audioFileBasename = path.basename(audioFile, path.extname(audioFile)).replace(/[^a-zA-Z0-9_-]/g, '_') // Replace non-alphanumeric chars with underscore
26
+ .replace(/_+/g, '_') // Replace multiple underscores with a single one
27
+ .trim();
28
+ logger.debug(`Processed audio filename: ${audioFileBasename}`);
29
+ let transcriptOutputFilename = await operator.constructFilename(creation, 'transcript', hash, {
30
+ subject: audioFileBasename
31
+ });
32
+ // Ensure the filename ends with .json
33
+ if (!transcriptOutputFilename.endsWith('.json')) {
34
+ logger.warn('constructFilename did not return a .json file for transcript, appending extension: %s', transcriptOutputFilename);
35
+ transcriptOutputFilename += '.json';
36
+ }
37
+ const transcriptOutputPath = path.join(interimPath, transcriptOutputFilename);
38
+ // Check if transcription already exists
39
+ if (await storage.exists(transcriptOutputPath)) {
40
+ logger.info('Transcription file %s already exists, returning existing content...', transcriptOutputPath);
41
+ const existingContent = await storage.readFile(transcriptOutputPath, 'utf8');
42
+ return JSON.parse(existingContent);
43
+ }
44
+ const baseDebugFilename = path.parse(transcriptOutputFilename).name;
45
+ const transcriptionDebugFile = config.debug ? path.join(interimPath, `${baseDebugFilename}.transcription.raw.response.json`) : undefined;
46
+ // Check if audio file exceeds the size limit
47
+ const fileSize = await media.getFileSize(audioFile);
48
+ logger.debug(`Audio file size: ${fileSize} bytes, max size: ${config.maxAudioSize} bytes`);
49
+ let transcription;
50
+ if (fileSize > config.maxAudioSize) {
51
+ logger.info(`Audio file exceeds maximum size (${fileSize} > ${config.maxAudioSize} bytes), splitting into chunks`);
52
+ // Create a temporary directory for the audio chunks
53
+ const tempDir = path.join(config.tempDirectory, `split_audio_${hash}`);
54
+ await storage.createDirectory(tempDir);
55
+ try {
56
+ // Split the audio file into chunks
57
+ const audioChunks = await media.splitAudioFile(audioFile, tempDir, config.maxAudioSize);
58
+ logger.info(`Split audio file into ${audioChunks.length} chunks`);
59
+ // Transcribe each chunk
60
+ const transcriptions = [];
61
+ for(let i = 0; i < audioChunks.length; i++){
62
+ const chunkPath = audioChunks[i];
63
+ logger.info(`Transcribing chunk ${i + 1}/${audioChunks.length}: ${chunkPath}`);
64
+ const chunkDebugFile = config.debug ? path.join(interimPath, `${baseDebugFilename}.transcription.chunk${i + 1}.raw.response.json`) : undefined;
65
+ const chunkTranscription = await transcribeAudio(chunkPath, {
66
+ model: config.transcriptionModel,
67
+ debug: config.debug,
68
+ debugFile: chunkDebugFile
69
+ });
70
+ transcriptions.push(chunkTranscription);
71
+ }
72
+ // Combine all transcriptions
73
+ const combinedText = transcriptions.map((t)=>t.text).join(' ');
74
+ transcription = {
75
+ text: combinedText
76
+ };
77
+ // Save each individual chunk for debugging
78
+ await storage.writeFile(path.join(interimPath, `${baseDebugFilename}.transcription.combined.json`), stringifyJSON({
79
+ chunks: transcriptions,
80
+ combined: transcription
81
+ }), 'utf8');
82
+ // Clean up temporary files if not in debug mode
83
+ if (!config.debug) {
84
+ for (const chunk of audioChunks){
85
+ try {
86
+ await storage.deleteFile(chunk);
87
+ } catch (error) {
88
+ logger.warn(`Failed to delete temporary chunk ${chunk}: ${error}`);
89
+ }
90
+ }
91
+ }
92
+ } catch (error) {
93
+ logger.error(`Error processing split audio files: ${error}`);
94
+ throw new Error(`Failed to process split audio files: ${error}`);
95
+ }
96
+ } else {
97
+ // If file size is within the limit, transcribe normally
98
+ transcription = await transcribeAudio(audioFile, {
99
+ model: config.transcriptionModel,
100
+ debug: config.debug,
101
+ debugFile: transcriptionDebugFile
102
+ });
103
+ }
104
+ // Save the transcription
105
+ await storage.writeFile(transcriptOutputPath, stringifyJSON(transcription), 'utf8');
106
+ logger.debug('Wrote transcription to %s', transcriptOutputPath);
107
+ // Create markdown version of the transcript
108
+ const markdownOutputFilename = transcriptOutputFilename.replace('.json', '.md');
109
+ const markdownOutputPath = path.join(outputPath, markdownOutputFilename);
110
+ // Only create the markdown file if it doesn't already exist
111
+ if (!await storage.exists(markdownOutputPath)) {
112
+ logger.info('Creating Markdown version of the transcription...');
113
+ // Create a prompt for the transcription formatting task
114
+ const prompt = await prompts.createTranscribePrompt(transcription.text);
115
+ // Format the prompt using the override utility
116
+ const formatter = Formatter.create();
117
+ const chatRequest = formatter.formatPrompt(config.model, prompt);
118
+ // Debug file paths for the request and response
119
+ const requestDebugFile = config.debug ? path.join(interimPath, `${baseDebugFilename}.markdown.request.json`) : undefined;
120
+ const responseDebugFile = config.debug ? path.join(interimPath, `${baseDebugFilename}.markdown.response.json`) : undefined;
121
+ // Write debug file for the request if in debug mode
122
+ if (config.debug && requestDebugFile) {
123
+ await storage.writeFile(requestDebugFile, stringifyJSON(chatRequest), 'utf8');
124
+ logger.debug('Wrote chat request to %s', requestDebugFile);
125
+ }
126
+ // Call the model to convert the transcription to markdown
127
+ const markdownContent = await createCompletion(chatRequest.messages, {
128
+ model: config.model,
129
+ debug: config.debug,
130
+ debugFile: responseDebugFile
131
+ });
132
+ // Save the markdown version
133
+ await storage.writeFile(markdownOutputPath, markdownContent, 'utf8');
134
+ logger.debug('Wrote markdown transcription to %s', markdownOutputPath);
135
+ } else {
136
+ logger.info('Markdown transcription file %s already exists, skipping...', markdownOutputPath);
137
+ }
138
+ return {
139
+ ...transcription,
140
+ audioFileBasename
141
+ };
142
+ };
143
+ return {
144
+ transcribe
145
+ };
146
+ };
147
+
148
+ export { create };
149
+ //# sourceMappingURL=transcribe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcribe.js","sources":["../../src/phases/transcribe.ts"],"sourcesContent":["import * as Dreadcabinet from '@theunwalked/dreadcabinet';\nimport { Config } from '@/protokoll';\nimport * as Logging from '@/logging';\nimport * as Storage from '@/util/storage';\nimport * as Media from '@/util/media';\nimport * as OpenAI from '@/util/openai';\nimport { stringifyJSON } from '@/util/general';\nimport path from 'path';\nimport { ChatCompletionMessageParam } from 'openai/resources';\nimport { Chat, Formatter } from '@riotprompt/riotprompt';\nimport * as TranscribePrompt from '@/prompt/transcribe';\n\nexport interface Transcription {\n text: string;\n audioFileBasename: string;\n}\n\nexport interface Instance {\n transcribe: (creation: Date, outputPath: string, contextPath: string, interimPath: string, filename: string, hash: string, audioFile: string) => Promise<Transcription>;\n}\n\nexport const create = (config: Config, operator: Dreadcabinet.Operator): Instance => {\n const logger = Logging.getLogger();\n const storage = Storage.create({ log: logger.debug });\n const media = Media.create(logger);\n const prompts = TranscribePrompt.create(config.model as Chat.Model, config);\n\n const transcribe = async (creation: Date, outputPath: string, contextPath: string, interimPath: string, filename: string, hash: string, audioFile: string): Promise<Transcription> => {\n if (!outputPath) {\n throw new Error(\"outputPath is required for transcribe function\");\n }\n\n if (!audioFile) {\n throw new Error(\"audioFile is required for transcribe function\");\n }\n\n // Remove extension from audioFile and make the name filesafe\n const audioFileBasename = path.basename(audioFile, path.extname(audioFile))\n .replace(/[^a-zA-Z0-9_-]/g, '_') // Replace non-alphanumeric chars with underscore\n .replace(/_+/g, '_') // Replace multiple underscores with a single one\n .trim();\n\n logger.debug(`Processed audio filename: ${audioFileBasename}`);\n\n let transcriptOutputFilename = await operator.constructFilename(creation, 'transcript', hash, { subject: audioFileBasename });\n // Ensure the filename ends with .json\n if (!transcriptOutputFilename.endsWith('.json')) {\n logger.warn('constructFilename did not return a .json file for transcript, appending extension: %s', transcriptOutputFilename);\n transcriptOutputFilename += '.json';\n }\n\n const transcriptOutputPath = path.join(interimPath, transcriptOutputFilename);\n\n // Check if transcription already exists\n if (await storage.exists(transcriptOutputPath)) {\n logger.info('Transcription file %s already exists, returning existing content...', transcriptOutputPath);\n const existingContent = await storage.readFile(transcriptOutputPath, 'utf8');\n return JSON.parse(existingContent);\n }\n\n const baseDebugFilename = path.parse(transcriptOutputFilename).name;\n const transcriptionDebugFile = config.debug ? path.join(interimPath, `${baseDebugFilename}.transcription.raw.response.json`) : undefined;\n\n // Check if audio file exceeds the size limit\n const fileSize = await media.getFileSize(audioFile);\n logger.debug(`Audio file size: ${fileSize} bytes, max size: ${config.maxAudioSize} bytes`);\n\n let transcription: OpenAI.Transcription;\n\n if (fileSize > config.maxAudioSize) {\n logger.info(`Audio file exceeds maximum size (${fileSize} > ${config.maxAudioSize} bytes), splitting into chunks`);\n\n // Create a temporary directory for the audio chunks\n const tempDir = path.join(config.tempDirectory, `split_audio_${hash}`);\n await storage.createDirectory(tempDir);\n\n try {\n // Split the audio file into chunks\n const audioChunks = await media.splitAudioFile(audioFile, tempDir, config.maxAudioSize);\n logger.info(`Split audio file into ${audioChunks.length} chunks`);\n\n // Transcribe each chunk\n const transcriptions: OpenAI.Transcription[] = [];\n for (let i = 0; i < audioChunks.length; i++) {\n const chunkPath = audioChunks[i];\n logger.info(`Transcribing chunk ${i + 1}/${audioChunks.length}: ${chunkPath}`);\n\n const chunkDebugFile = config.debug ?\n path.join(interimPath, `${baseDebugFilename}.transcription.chunk${i + 1}.raw.response.json`) :\n undefined;\n\n const chunkTranscription = await OpenAI.transcribeAudio(chunkPath, {\n model: config.transcriptionModel,\n debug: config.debug,\n debugFile: chunkDebugFile\n });\n\n transcriptions.push(chunkTranscription);\n }\n\n // Combine all transcriptions\n const combinedText = transcriptions.map(t => t.text).join(' ');\n transcription = { text: combinedText };\n\n // Save each individual chunk for debugging\n await storage.writeFile(\n path.join(interimPath, `${baseDebugFilename}.transcription.combined.json`),\n stringifyJSON({ chunks: transcriptions, combined: transcription }),\n 'utf8'\n );\n\n // Clean up temporary files if not in debug mode\n if (!config.debug) {\n for (const chunk of audioChunks) {\n try {\n await storage.deleteFile(chunk);\n } catch (error) {\n logger.warn(`Failed to delete temporary chunk ${chunk}: ${error}`);\n }\n }\n }\n } catch (error) {\n logger.error(`Error processing split audio files: ${error}`);\n throw new Error(`Failed to process split audio files: ${error}`);\n }\n } else {\n // If file size is within the limit, transcribe normally\n transcription = await OpenAI.transcribeAudio(audioFile, {\n model: config.transcriptionModel,\n debug: config.debug,\n debugFile: transcriptionDebugFile\n });\n }\n\n // Save the transcription\n await storage.writeFile(transcriptOutputPath, stringifyJSON(transcription), 'utf8');\n logger.debug('Wrote transcription to %s', transcriptOutputPath);\n\n // Create markdown version of the transcript\n const markdownOutputFilename = transcriptOutputFilename.replace('.json', '.md');\n const markdownOutputPath = path.join(outputPath, markdownOutputFilename);\n\n // Only create the markdown file if it doesn't already exist\n if (!await storage.exists(markdownOutputPath)) {\n logger.info('Creating Markdown version of the transcription...');\n\n // Create a prompt for the transcription formatting task\n const prompt = await prompts.createTranscribePrompt(transcription.text);\n\n // Format the prompt using the override utility\n const formatter = Formatter.create();\n const chatRequest: Chat.Request = formatter.formatPrompt(config.model as Chat.Model, prompt);\n\n // Debug file paths for the request and response\n const requestDebugFile = config.debug ?\n path.join(interimPath, `${baseDebugFilename}.markdown.request.json`) :\n undefined;\n\n const responseDebugFile = config.debug ?\n path.join(interimPath, `${baseDebugFilename}.markdown.response.json`) :\n undefined;\n\n // Write debug file for the request if in debug mode\n if (config.debug && requestDebugFile) {\n await storage.writeFile(requestDebugFile, stringifyJSON(chatRequest), 'utf8');\n logger.debug('Wrote chat request to %s', requestDebugFile);\n }\n\n // Call the model to convert the transcription to markdown\n const markdownContent = await OpenAI.createCompletion(\n chatRequest.messages as ChatCompletionMessageParam[],\n {\n model: config.model,\n debug: config.debug,\n debugFile: responseDebugFile\n }\n );\n\n // Save the markdown version\n await storage.writeFile(markdownOutputPath, markdownContent, 'utf8');\n logger.debug('Wrote markdown transcription to %s', markdownOutputPath);\n } else {\n logger.info('Markdown transcription file %s already exists, skipping...', markdownOutputPath);\n }\n\n return {\n ...transcription,\n audioFileBasename,\n };\n }\n\n return {\n transcribe,\n }\n} "],"names":["create","config","operator","logger","Logging","storage","Storage","log","debug","media","Media","prompts","TranscribePrompt","model","transcribe","creation","outputPath","contextPath","interimPath","filename","hash","audioFile","Error","audioFileBasename","path","basename","extname","replace","trim","transcriptOutputFilename","constructFilename","subject","endsWith","warn","transcriptOutputPath","join","exists","info","existingContent","readFile","JSON","parse","baseDebugFilename","name","transcriptionDebugFile","undefined","fileSize","getFileSize","maxAudioSize","transcription","tempDir","tempDirectory","createDirectory","audioChunks","splitAudioFile","length","transcriptions","i","chunkPath","chunkDebugFile","chunkTranscription","OpenAI","transcriptionModel","debugFile","push","combinedText","map","t","text","writeFile","stringifyJSON","chunks","combined","chunk","deleteFile","error","markdownOutputFilename","markdownOutputPath","prompt","createTranscribePrompt","formatter","Formatter","chatRequest","formatPrompt","requestDebugFile","responseDebugFile","markdownContent","messages"],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { getLogger } from './logging.js';
2
+ import { create as create$1 } from './phases/transcribe.js';
3
+ import { create as create$2 } from './phases/locate.js';
4
+
5
+ const create = (config, operator)=>{
6
+ const logger = getLogger();
7
+ const transcribePhase = create$1(config, operator);
8
+ const locatePhase = create$2(config, operator);
9
+ const process = async (audioFile)=>{
10
+ logger.verbose('Processing file %s', audioFile);
11
+ // Locate the contents in time and on the filesystem
12
+ logger.debug('Locating file %s', audioFile);
13
+ const { creationTime, outputPath, contextPath, interimPath, transcriptionFilename, hash } = await locatePhase.locate(audioFile);
14
+ logger.debug('Locate complete: %s', JSON.stringify({
15
+ creationTime,
16
+ outputPath,
17
+ contextPath,
18
+ interimPath,
19
+ transcriptionFilename,
20
+ hash
21
+ }));
22
+ // Transcribe the audio
23
+ logger.debug('Transcribing file %s', audioFile);
24
+ await transcribePhase.transcribe(creationTime, outputPath, contextPath, interimPath, transcriptionFilename, hash, audioFile);
25
+ logger.info('Transcription complete for file %s', audioFile);
26
+ logger.info('Transcription saved to: %s', transcriptionFilename);
27
+ return;
28
+ };
29
+ return {
30
+ process
31
+ };
32
+ };
33
+
34
+ export { create };
35
+ //# sourceMappingURL=processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processor.js","sources":["../src/processor.ts"],"sourcesContent":["import * as Logging from '@/logging';\nimport * as TranscribePhase from '@/phases/transcribe';\nimport * as LocatePhase from '@/phases/locate';\nimport * as Dreadcabinet from '@theunwalked/dreadcabinet';\nimport { Config } from '@/protokoll';\n\nexport interface Transcription {\n text: string;\n audioFileBasename: string;\n}\n\nexport interface Instance {\n process(file: string): Promise<void>;\n}\n\nexport const create = (config: Config, operator: Dreadcabinet.Operator): Instance => {\n const logger = Logging.getLogger();\n\n const transcribePhase: TranscribePhase.Instance = TranscribePhase.create(config, operator);\n const locatePhase: LocatePhase.Instance = LocatePhase.create(config, operator);\n\n const process = async (audioFile: string) => {\n logger.verbose('Processing file %s', audioFile);\n\n // Locate the contents in time and on the filesystem\n logger.debug('Locating file %s', audioFile);\n const { creationTime, outputPath, contextPath, interimPath, transcriptionFilename, hash } = await locatePhase.locate(audioFile);\n logger.debug('Locate complete: %s', JSON.stringify({ creationTime, outputPath, contextPath, interimPath, transcriptionFilename, hash }));\n\n // Transcribe the audio\n logger.debug('Transcribing file %s', audioFile);\n await transcribePhase.transcribe(creationTime, outputPath, contextPath, interimPath, transcriptionFilename, hash, audioFile);\n\n logger.info('Transcription complete for file %s', audioFile);\n logger.info('Transcription saved to: %s', transcriptionFilename);\n return;\n }\n\n return {\n process,\n }\n}\n\n\n"],"names":["create","config","operator","logger","Logging","transcribePhase","TranscribePhase","locatePhase","LocatePhase","process","audioFile","verbose","debug","creationTime","outputPath","contextPath","interimPath","transcriptionFilename","hash","locate","JSON","stringify","transcribe","info"],"mappings":";;;;AAeO,MAAMA,MAAAA,GAAS,CAACC,MAAAA,EAAgBC,QAAAA,GAAAA;IACnC,MAAMC,MAAAA,GAASC,SAAiB,EAAA;AAEhC,IAAA,MAAMC,eAAAA,GAA4CC,QAAsB,CAACL,MAAAA,EAAQC,QAAAA,CAAAA;AACjF,IAAA,MAAMK,WAAAA,GAAoCC,QAAkB,CAACP,MAAAA,EAAQC,QAAAA,CAAAA;AAErE,IAAA,MAAMO,UAAU,OAAOC,SAAAA,GAAAA;QACnBP,MAAAA,CAAOQ,OAAO,CAAC,oBAAA,EAAsBD,SAAAA,CAAAA;;QAGrCP,MAAAA,CAAOS,KAAK,CAAC,kBAAA,EAAoBF,SAAAA,CAAAA;AACjC,QAAA,MAAM,EAAEG,YAAY,EAAEC,UAAU,EAAEC,WAAW,EAAEC,WAAW,EAAEC,qBAAqB,EAAEC,IAAI,EAAE,GAAG,MAAMX,WAAAA,CAAYY,MAAM,CAACT,SAAAA,CAAAA;AACrHP,QAAAA,MAAAA,CAAOS,KAAK,CAAC,qBAAA,EAAuBQ,IAAAA,CAAKC,SAAS,CAAC;AAAER,YAAAA,YAAAA;AAAcC,YAAAA,UAAAA;AAAYC,YAAAA,WAAAA;AAAaC,YAAAA,WAAAA;AAAaC,YAAAA,qBAAAA;AAAuBC,YAAAA;AAAK,SAAA,CAAA,CAAA;;QAGrIf,MAAAA,CAAOS,KAAK,CAAC,sBAAA,EAAwBF,SAAAA,CAAAA;QACrC,MAAML,eAAAA,CAAgBiB,UAAU,CAACT,YAAAA,EAAcC,YAAYC,WAAAA,EAAaC,WAAAA,EAAaC,uBAAuBC,IAAAA,EAAMR,SAAAA,CAAAA;QAElHP,MAAAA,CAAOoB,IAAI,CAAC,oCAAA,EAAsCb,SAAAA,CAAAA;QAClDP,MAAAA,CAAOoB,IAAI,CAAC,4BAAA,EAA8BN,qBAAAA,CAAAA;AAC1C,QAAA;AACJ,IAAA,CAAA;IAEA,OAAO;AACHR,QAAAA;AACJ,KAAA;AACJ;;;;"}
@@ -0,0 +1,46 @@
1
+ ## 🧠 Whisper Transcript Post-Processor Prompt
2
+
3
+ You're a transcript formatting tool, not a summarizer, editor, or rewriter.
4
+
5
+ You will receive a raw transcript from Whisper. Your task is to convert it into **clean, readable Markdown** with **intelligent paragraph breaks**, optional **section headings**, and accurate spelling of names and concepts.
6
+
7
+ ### 📝 Output Format
8
+
9
+ - Output **MUST be in Markdown**.
10
+ - Use **`#`, `##`, or `###` headings** to group content into logical sections **if** a topic shift is clearly identifiable.
11
+ - Insert **paragraph breaks** in appropriate places to improve readability, particularly:
12
+ - at topic shifts
13
+ - after long pauses or asides
14
+ - between distinct ideas
15
+ - Do **not** summarize, shorten, or omit anything unless it's clearly repetitive or a verbal filler (e.g. "uh", "you know", "like" used in isolation).
16
+ - Do **not** embellish or market language. For example, do not rephrase “this might work” as “this innovative idea…”
17
+
18
+ ### 🔍 Fidelity Requirements
19
+
20
+ - **Do not simplify or reinterpret the speaker’s intent.**
21
+ - Do not remove technical details, curse words, or hedged or tentative phrasing.
22
+ - Preserve filler words **if they contribute to tone or meaning** (e.g. “I mean”, “sort of”, “well”), but collapse **repetitions** of the exact same phrase if clearly unintentional.
23
+
24
+ ### ✏️ Spelling & Entity Correction
25
+
26
+ Use the **provided context** (e.g. glossary, list of names, known topics) to:
27
+
28
+ - Correct spelling of **people's names**, **company names**, **tools**, or **technical terms** that Whisper might get wrong.
29
+ - Example: if the context includes “Adrian Sloan” and the transcript says “Adreean Slohn”, correct it to “Adrian Sloan”.
30
+
31
+ If you are uncertain about a correction, include the likely correct term with the original in parentheses:
32
+ e.g. `Adrian Sloan (transcript: "Adreean Slohn")`
33
+
34
+ ### 🚫 Do Not:
35
+
36
+ - Do not shorten the transcript.
37
+ - Do not summarize.
38
+ - Do not interpret tone or intent.
39
+ - Do not turn notes into copy.
40
+ - Do not hallucinate or "fix" awkward phrasing unless it's an obvious transcription error.
41
+
42
+ ### ✅ Do:
43
+
44
+ - Maintain all nuance.
45
+ - Correct mistranscribed words or names using context.
46
+ - Output clean, readable Markdown for humans or downstream systems to use.
@@ -0,0 +1,37 @@
1
+ ## 🧑‍💼 Persona: TranscriptFormatter-v1
2
+
3
+ **Role**: Markdown transcript formatter and error-correction agent.
4
+
5
+ **Purpose**: To convert raw, unstructured Whisper-generated transcripts into structured, readable, and **high-fidelity** Markdown documents suitable for human review or downstream system processing.
6
+
7
+ ### 🎯 Core Traits
8
+
9
+ - **Literal** – Captures exactly what was said, not what *should have* been said.
10
+ - **Structured** – Organizes content with paragraphs and optional section headings without altering meaning.
11
+ - **Context-aware** – Uses external context to resolve proper names, technical terms, and common transcription errors.
12
+ - **Anti-summarizer** – Never reduces, condenses, or editorializes.
13
+ - **Language-fidelity obsessed** – Preserves the tone, hesitations, repetitions (unless clearly unintentional), and casual phrasing.
14
+
15
+ ### 🧱 Boundaries
16
+
17
+ - Will **not** reword awkward phrasing for style.
18
+ - Will **not** remove profanity, hedging, or emotion unless explicitly instructed.
19
+ - Will **not** guess or extrapolate beyond context or transcript.
20
+
21
+ ### 🧰 Toolkit
22
+
23
+ - Markdown formatting engine (headings, paragraphs, emphasis).
24
+ - Entity correction using supplied glossary/context.
25
+ - Repetition collapse (only when verbatim duplication is evident).
26
+ - Parenthetical disambiguation when corrections are uncertain.
27
+
28
+ ### ✅ Example Behavior
29
+
30
+ - Transcript says: `"uh we talked to adreean slohn yesterday about the update thing"`
31
+ - Context lists: `Adrian Sloan`
32
+ - Output:
33
+ `"We talked to Adrian Sloan (transcript: "adreean slohn") yesterday about the update thing."`
34
+
35
+ ---
36
+
37
+ *TranscriptFormatter-v1 is not a creative assistant. It is a high-accuracy Markdown transcription formatter trained to obey literal constraints and structural cues.*
@@ -0,0 +1,41 @@
1
+ import { Builder } from '@riotprompt/riotprompt';
2
+ import { DEFAULT_PERSONA_TRANSCRIBER_FILE, DEFAULT_INSTRUCTIONS_TRANSCRIBE_FILE } from '../constants.js';
3
+ import { fileURLToPath } from 'url';
4
+ import path from 'path';
5
+ import { getLogger } from '../logging.js';
6
+
7
+ const __filename$1 = fileURLToPath(import.meta.url);
8
+ const __dirname$1 = path.dirname(__filename$1);
9
+ /**
10
+ * Creates a prompt for the transcription formatting task
11
+ */ const createTranscribePrompt = async (transcriptionText, config)=>{
12
+ const logger = getLogger();
13
+ let builder = Builder.create({
14
+ logger,
15
+ basePath: __dirname$1,
16
+ overridePaths: [
17
+ config.configDirectory
18
+ ],
19
+ overrides: config.overrides
20
+ });
21
+ builder = await builder.addPersonaPath(DEFAULT_PERSONA_TRANSCRIBER_FILE);
22
+ builder = await builder.addInstructionPath(DEFAULT_INSTRUCTIONS_TRANSCRIBE_FILE);
23
+ builder = await builder.addContent(transcriptionText);
24
+ if (config.contextDirectories) {
25
+ builder = await builder.loadContext(config.contextDirectories);
26
+ }
27
+ const prompt = await builder.build();
28
+ return prompt;
29
+ };
30
+ /**
31
+ * Create a factory for transcribe prompts
32
+ */ const create = (model, config)=>{
33
+ return {
34
+ createTranscribePrompt: async (transcriptionText)=>{
35
+ return createTranscribePrompt(transcriptionText, config);
36
+ }
37
+ };
38
+ };
39
+
40
+ export { create, createTranscribePrompt };
41
+ //# sourceMappingURL=transcribe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcribe.js","sources":["../../src/prompt/transcribe.ts"],"sourcesContent":["import { Builder, Chat, Prompt } from \"@riotprompt/riotprompt\";\nimport { DEFAULT_INSTRUCTIONS_TRANSCRIBE_FILE, DEFAULT_PERSONA_TRANSCRIBER_FILE } from '@/constants';\nimport { Config } from '@/protokoll';\nimport { fileURLToPath } from \"url\";\nimport path from \"path\";\nimport { getLogger } from \"@/logging\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n/**\n * Creates a prompt for the transcription formatting task\n */\nexport const createTranscribePrompt = async (\n transcriptionText: string,\n config: Config\n): Promise<Prompt> => {\n const logger = getLogger();\n let builder: Builder.Instance = Builder.create({ logger, basePath: __dirname, overridePaths: [config.configDirectory], overrides: config.overrides });\n builder = await builder.addPersonaPath(DEFAULT_PERSONA_TRANSCRIBER_FILE);\n builder = await builder.addInstructionPath(DEFAULT_INSTRUCTIONS_TRANSCRIBE_FILE);\n builder = await builder.addContent(transcriptionText);\n if (config.contextDirectories) {\n builder = await builder.loadContext(config.contextDirectories);\n }\n\n const prompt = await builder.build();\n return prompt;\n};\n\n/**\n * Factory interface for transcribe prompts\n */\nexport interface Factory {\n createTranscribePrompt: (transcriptionText: string) => Promise<Prompt>;\n}\n\n/**\n * Create a factory for transcribe prompts\n */\nexport const create = (model: Chat.Model, config: Config): Factory => {\n return {\n createTranscribePrompt: async (transcriptionText: string): Promise<Prompt> => {\n return createTranscribePrompt(transcriptionText, config);\n }\n };\n}; "],"names":["__filename","fileURLToPath","url","__dirname","path","dirname","createTranscribePrompt","transcriptionText","config","logger","getLogger","builder","Builder","create","basePath","overridePaths","configDirectory","overrides","addPersonaPath","DEFAULT_PERSONA_TRANSCRIBER_FILE","addInstructionPath","DEFAULT_INSTRUCTIONS_TRANSCRIBE_FILE","addContent","contextDirectories","loadContext","prompt","build","model"],"mappings":";;;;;;AAOA,MAAMA,YAAAA,GAAaC,aAAAA,CAAc,MAAA,CAAA,IAAA,CAAYC,GAAG,CAAA;AAChD,MAAMC,WAAAA,GAAYC,IAAAA,CAAKC,OAAO,CAACL,YAAAA,CAAAA;AAE/B;;AAEC,IACM,MAAMM,sBAAAA,GAAyB,OAClCC,iBAAAA,EACAC,MAAAA,GAAAA;AAEA,IAAA,MAAMC,MAAAA,GAASC,SAAAA,EAAAA;IACf,IAAIC,OAAAA,GAA4BC,OAAAA,CAAQC,MAAM,CAAC;AAAEJ,QAAAA,MAAAA;QAAQK,QAAAA,EAAUX,WAAAA;QAAWY,aAAAA,EAAe;AAACP,YAAAA,MAAAA,CAAOQ;AAAgB,SAAA;AAAEC,QAAAA,SAAAA,EAAWT,OAAOS;AAAU,KAAA,CAAA;IACnJN,OAAAA,GAAU,MAAMA,OAAAA,CAAQO,cAAc,CAACC,gCAAAA,CAAAA;IACvCR,OAAAA,GAAU,MAAMA,OAAAA,CAAQS,kBAAkB,CAACC,oCAAAA,CAAAA;IAC3CV,OAAAA,GAAU,MAAMA,OAAAA,CAAQW,UAAU,CAACf,iBAAAA,CAAAA;IACnC,IAAIC,MAAAA,CAAOe,kBAAkB,EAAE;AAC3BZ,QAAAA,OAAAA,GAAU,MAAMA,OAAAA,CAAQa,WAAW,CAAChB,OAAOe,kBAAkB,CAAA;AACjE,IAAA;IAEA,MAAME,MAAAA,GAAS,MAAMd,OAAAA,CAAQe,KAAK,EAAA;IAClC,OAAOD,MAAAA;AACX;AASA;;AAEC,IACM,MAAMZ,MAAAA,GAAS,CAACc,KAAAA,EAAmBnB,MAAAA,GAAAA;IACtC,OAAO;AACHF,QAAAA,sBAAAA,EAAwB,OAAOC,iBAAAA,GAAAA;AAC3B,YAAA,OAAOD,uBAAuBC,iBAAAA,EAAmBC,MAAAA,CAAAA;AACrD,QAAA;AACJ,KAAA;AACJ;;;;"}
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+ import 'dotenv/config';
3
+ import { configure } from './arguments.js';
4
+ import { PROGRAM_NAME, VERSION, ALLOWED_OUTPUT_FILENAME_OPTIONS, ALLOWED_OUTPUT_STRUCTURES, ALLOWED_AUDIO_EXTENSIONS, DEFAULT_OUTPUT_DIRECTORY, DEFAULT_INPUT_DIRECTORY, DEFAULT_OUTPUT_FILENAME_OPTIONS, DEFAULT_OUTPUT_STRUCTURE, DEFAULT_AUDIO_EXTENSIONS, DEFAULT_TIMEZONE, DEFAULT_CONFIG_DIR } from './constants.js';
5
+ import { setLogLevel, getLogger } from './logging.js';
6
+ import { create } from './processor.js';
7
+ import * as Dreadcabinet from '@theunwalked/dreadcabinet';
8
+ import * as Cardigantime from '@theunwalked/cardigantime';
9
+ import { z } from 'zod';
10
+
11
+ const ConfigSchema = z.object({
12
+ dryRun: z.boolean(),
13
+ verbose: z.boolean(),
14
+ debug: z.boolean(),
15
+ diff: z.boolean(),
16
+ log: z.boolean(),
17
+ model: z.string(),
18
+ transcriptionModel: z.string(),
19
+ contentTypes: z.array(z.string()),
20
+ overrides: z.boolean(),
21
+ contextDirectories: z.array(z.string()).optional(),
22
+ maxAudioSize: z.number(),
23
+ tempDirectory: z.string()
24
+ });
25
+ z.object({
26
+ openaiApiKey: z.string().optional()
27
+ });
28
+ async function main() {
29
+ // eslint-disable-next-line no-console
30
+ console.info(`Starting ${PROGRAM_NAME}: ${VERSION}`);
31
+ const dreadcabinetOptions = {
32
+ defaults: {
33
+ timezone: DEFAULT_TIMEZONE,
34
+ extensions: DEFAULT_AUDIO_EXTENSIONS,
35
+ outputStructure: DEFAULT_OUTPUT_STRUCTURE,
36
+ outputFilenameOptions: DEFAULT_OUTPUT_FILENAME_OPTIONS,
37
+ inputDirectory: DEFAULT_INPUT_DIRECTORY,
38
+ outputDirectory: DEFAULT_OUTPUT_DIRECTORY
39
+ },
40
+ allowed: {
41
+ extensions: ALLOWED_AUDIO_EXTENSIONS,
42
+ outputStructures: ALLOWED_OUTPUT_STRUCTURES,
43
+ outputFilenameOptions: ALLOWED_OUTPUT_FILENAME_OPTIONS
44
+ },
45
+ features: Dreadcabinet.DEFAULT_FEATURES,
46
+ addDefaults: false
47
+ };
48
+ const dreadcabinet = Dreadcabinet.create(dreadcabinetOptions);
49
+ const cardigantime = Cardigantime.create({
50
+ defaults: {
51
+ configDirectory: DEFAULT_CONFIG_DIR
52
+ },
53
+ configShape: ConfigSchema.shape
54
+ });
55
+ const [config, secureConfig] = await configure(dreadcabinet, cardigantime);
56
+ // Set log level based on verbose flag
57
+ if (config.verbose === true) {
58
+ setLogLevel('verbose');
59
+ }
60
+ if (config.debug === true) {
61
+ setLogLevel('debug');
62
+ }
63
+ const logger = getLogger();
64
+ dreadcabinet.setLogger(logger);
65
+ try {
66
+ const operator = await dreadcabinet.operate({
67
+ ...config,
68
+ ...secureConfig
69
+ });
70
+ const processor = create(config, operator);
71
+ await operator.process(async (file)=>{
72
+ await processor.process(file);
73
+ });
74
+ } catch (error) {
75
+ logger.error('Exiting due to Error: %s, %s', error.message, error.stack);
76
+ process.exit(1);
77
+ }
78
+ }
79
+
80
+ export { ConfigSchema, main };
81
+ //# sourceMappingURL=protokoll.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protokoll.js","sources":["../src/protokoll.ts"],"sourcesContent":["#!/usr/bin/env node\nimport 'dotenv/config';\nimport * as Arguments from '@/arguments';\nimport { ALLOWED_AUDIO_EXTENSIONS, ALLOWED_OUTPUT_FILENAME_OPTIONS, ALLOWED_OUTPUT_STRUCTURES, DEFAULT_AUDIO_EXTENSIONS, DEFAULT_OUTPUT_FILENAME_OPTIONS, DEFAULT_INPUT_DIRECTORY, DEFAULT_OUTPUT_DIRECTORY, DEFAULT_OUTPUT_STRUCTURE, DEFAULT_TIMEZONE, PROGRAM_NAME, VERSION, DEFAULT_CONFIG_DIR } from '@/constants';\nimport { getLogger, setLogLevel } from '@/logging';\nimport * as Processor from './processor';\nimport * as Dreadcabinet from '@theunwalked/dreadcabinet';\nimport * as Cardigantime from '@theunwalked/cardigantime';\nimport { z } from 'zod';\n\nexport interface Args extends Dreadcabinet.Args, Cardigantime.Args {\n dryRun?: boolean;\n verbose?: boolean;\n debug?: boolean;\n transcriptionModel?: string;\n model?: string;\n openaiApiKey?: string;\n overrides?: boolean;\n contextDirectories?: string[];\n maxAudioSize?: number | string;\n tempDirectory?: string;\n}\n\nexport const ConfigSchema = z.object({\n dryRun: z.boolean(),\n verbose: z.boolean(),\n debug: z.boolean(),\n diff: z.boolean(),\n log: z.boolean(),\n model: z.string(),\n transcriptionModel: z.string(),\n contentTypes: z.array(z.string()),\n overrides: z.boolean(),\n contextDirectories: z.array(z.string()).optional(),\n maxAudioSize: z.number(),\n tempDirectory: z.string(),\n});\n\nexport const SecureConfigSchema = z.object({\n openaiApiKey: z.string().optional(),\n});\n\nexport type Config = z.infer<typeof ConfigSchema> & Dreadcabinet.Config & Cardigantime.Config;\nexport type SecureConfig = z.infer<typeof SecureConfigSchema>;\n\nexport async function main() {\n\n // eslint-disable-next-line no-console\n console.info(`Starting ${PROGRAM_NAME}: ${VERSION}`);\n\n const dreadcabinetOptions = {\n defaults: {\n timezone: DEFAULT_TIMEZONE,\n extensions: DEFAULT_AUDIO_EXTENSIONS,\n outputStructure: DEFAULT_OUTPUT_STRUCTURE,\n outputFilenameOptions: DEFAULT_OUTPUT_FILENAME_OPTIONS,\n inputDirectory: DEFAULT_INPUT_DIRECTORY,\n outputDirectory: DEFAULT_OUTPUT_DIRECTORY,\n },\n allowed: {\n extensions: ALLOWED_AUDIO_EXTENSIONS,\n outputStructures: ALLOWED_OUTPUT_STRUCTURES,\n outputFilenameOptions: ALLOWED_OUTPUT_FILENAME_OPTIONS,\n },\n features: Dreadcabinet.DEFAULT_FEATURES,\n addDefaults: false,\n };\n\n const dreadcabinet = Dreadcabinet.create(dreadcabinetOptions);\n\n const cardigantime = Cardigantime.create({\n defaults: {\n configDirectory: DEFAULT_CONFIG_DIR,\n },\n configShape: ConfigSchema.shape,\n });\n\n const [config, secureConfig]: [Config, SecureConfig] = await Arguments.configure(dreadcabinet, cardigantime);\n\n // Set log level based on verbose flag\n if (config.verbose === true) {\n setLogLevel('verbose');\n }\n if (config.debug === true) {\n setLogLevel('debug');\n }\n\n const logger = getLogger();\n dreadcabinet.setLogger(logger);\n\n try {\n\n const operator: Dreadcabinet.Operator = await dreadcabinet.operate({\n ...config,\n ...secureConfig,\n });\n const processor = Processor.create(config, operator);\n\n await operator.process(async (file: string) => {\n await processor.process(file);\n });\n } catch (error: any) {\n logger.error('Exiting due to Error: %s, %s', error.message, error.stack);\n process.exit(1);\n }\n}"],"names":["ConfigSchema","z","object","dryRun","boolean","verbose","debug","diff","log","model","string","transcriptionModel","contentTypes","array","overrides","contextDirectories","optional","maxAudioSize","number","tempDirectory","openaiApiKey","main","console","info","PROGRAM_NAME","VERSION","dreadcabinetOptions","defaults","timezone","DEFAULT_TIMEZONE","extensions","DEFAULT_AUDIO_EXTENSIONS","outputStructure","DEFAULT_OUTPUT_STRUCTURE","outputFilenameOptions","DEFAULT_OUTPUT_FILENAME_OPTIONS","inputDirectory","DEFAULT_INPUT_DIRECTORY","outputDirectory","DEFAULT_OUTPUT_DIRECTORY","allowed","ALLOWED_AUDIO_EXTENSIONS","outputStructures","ALLOWED_OUTPUT_STRUCTURES","ALLOWED_OUTPUT_FILENAME_OPTIONS","features","Dreadcabinet","DEFAULT_FEATURES","addDefaults","dreadcabinet","create","cardigantime","Cardigantime","configDirectory","DEFAULT_CONFIG_DIR","configShape","shape","config","secureConfig","Arguments","setLogLevel","logger","getLogger","setLogger","operator","operate","processor","Processor","process","file","error","message","stack","exit"],"mappings":"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"}