@redaksjon/protokoll-engine 0.1.13 → 0.1.14-dev.20260304145509.97a25fe
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/index10.js +1 -1
- package/dist/index15.js +2 -1
- package/dist/index15.js.map +1 -1
- package/dist/index33.js +1 -1
- package/dist/index34.js +2 -2
- package/dist/index35.js +4 -4
- package/dist/index36.js +1 -1
- package/dist/index38.js +4 -2
- package/dist/index38.js.map +1 -1
- package/dist/index41.js +24 -1
- package/dist/index41.js.map +1 -1
- package/dist/index53.js +48 -5
- package/dist/index53.js.map +1 -1
- package/dist/index54.js +34 -46
- package/dist/index54.js.map +1 -1
- package/dist/index55.js +280 -35
- package/dist/index55.js.map +1 -1
- package/dist/index56.js +137 -258
- package/dist/index56.js.map +1 -1
- package/dist/index57.js +60 -142
- package/dist/index57.js.map +1 -1
- package/dist/index58.js +70 -73
- package/dist/index58.js.map +1 -1
- package/dist/index59.js +3 -73
- package/dist/index59.js.map +1 -1
- package/dist/index60.js +5 -148
- package/dist/index60.js.map +1 -1
- package/dist/index61.js +4 -4
- package/dist/index62.js +148 -5
- package/dist/index62.js.map +1 -1
- package/dist/out/manager.d.ts.map +1 -1
- package/dist/transcript/operations.d.ts +1 -1
- package/dist/transcript/operations.d.ts.map +1 -1
- package/dist/transcript/upload-utils.d.ts +9 -1
- package/dist/transcript/upload-utils.d.ts.map +1 -1
- package/dist/util/metadata.d.ts +2 -1
- package/dist/util/metadata.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index10.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { combineTranscripts, editTranscript, extractTimestampFromFilename, findTranscriptByUuid, isUuidInput, isValidStatusTransition, listTranscripts, parseTranscript, slugifyTitle } from './index38.js';
|
|
2
2
|
export { FEEDBACK_TOOLS, applyChanges, buildFeedbackSystemPrompt, executeTool, processFeedback } from './index39.js';
|
|
3
3
|
export { convertPklMetadataToLegacy, ensurePklExtension, getTranscriptGlobPattern, isPklFile, readTranscriptContent, resolveTranscriptPath, stripTranscriptExtension, transcriptExists, transcriptExistsUuid } from './index40.js';
|
|
4
|
-
export { createUploadTranscript, findTranscribingTranscripts, findUploadedTranscripts, generateFilenameWithUuid, markTranscriptAsFailed, markTranscriptAsTranscribing, resetTranscriptToUploaded } from './index41.js';
|
|
4
|
+
export { createUploadTranscript, findTranscribingTranscripts, findTranscriptByAudioHash, findUploadedTranscripts, generateFilenameWithUuid, markTranscriptAsFailed, markTranscriptAsTranscribing, resetTranscriptToUploaded } from './index41.js';
|
|
5
5
|
//# sourceMappingURL=index10.js.map
|
package/dist/index15.js
CHANGED
package/dist/index15.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index15.js","sources":["../src/util/metadata.ts"],"sourcesContent":["import * as Routing from '@/routing';\n\n// ============================================================================\n// Lifecycle Types\n// ============================================================================\n\n/**\n * Transcript lifecycle status\n * - initial: Whisper transcription complete\n * - enhanced: Context-aware enhancement complete\n * - reviewed: User has reviewed the transcript\n * - in_progress: Has outstanding tasks to complete\n * - closed: All work complete, no pending tasks\n * - archived: Archived for long-term storage\n */\nexport type TranscriptStatus = 'initial' | 'enhanced' | 'reviewed' | 'in_progress' | 'closed' | 'archived';\n\n/**\n * Record of a status transition with timestamp\n */\nexport interface StatusTransition {\n from: TranscriptStatus;\n to: TranscriptStatus;\n at: string; // ISO 8601 timestamp\n}\n\n/**\n * A follow-up task associated with a transcript\n */\nexport interface Task {\n id: string; // generated unique ID (e.g., task-1234567890-abc123)\n description: string;\n status: 'open' | 'done';\n created: string; // ISO 8601 timestamp\n changed?: string; // ISO 8601 timestamp - when last modified\n completed?: string; // ISO 8601 timestamp - when marked done\n}\n\n// ============================================================================\n// Entity Types\n// ============================================================================\n\nexport interface EntityReference {\n id: string;\n name: string;\n type: 'person' | 'project' | 'term' | 'company';\n}\n\nexport interface TranscriptMetadata {\n // UUID identifier\n id?: string;\n \n // Core fields\n title?: string;\n project?: string;\n projectId?: string;\n routing?: RoutingMetadata;\n tags?: string[];\n date?: Date;\n recordingTime?: string;\n confidence?: number;\n duration?: string;\n \n // Lifecycle fields\n status?: TranscriptStatus;\n history?: StatusTransition[];\n tasks?: Task[];\n \n // Entity references - entities mentioned/used in this transcript\n entities?: {\n people?: EntityReference[];\n projects?: EntityReference[];\n terms?: EntityReference[];\n companies?: EntityReference[];\n };\n}\n\nexport interface RoutingMetadata {\n destination: string;\n confidence: number;\n signals: Routing.ClassificationSignal[];\n reasoning: string;\n}\n\n/**\n * Format metadata as Markdown heading section\n */\nexport const formatMetadataMarkdown = (metadata: TranscriptMetadata): string => {\n const lines: string[] = [];\n \n // Title section\n if (metadata.title) {\n lines.push(`# ${metadata.title}`);\n lines.push('');\n }\n \n // Metadata frontmatter as readable markdown\n lines.push('## Metadata');\n lines.push('');\n \n // Date and Time\n if (metadata.date) {\n const dateStr = metadata.date.toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n });\n lines.push(`**Date**: ${dateStr}`);\n \n if (metadata.recordingTime) {\n lines.push(`**Time**: ${metadata.recordingTime}`);\n } else {\n const timeStr = metadata.date.toLocaleTimeString('en-US', {\n hour: '2-digit',\n minute: '2-digit',\n hour12: true\n });\n lines.push(`**Time**: ${timeStr}`);\n }\n }\n \n lines.push('');\n \n // Project\n if (metadata.project) {\n lines.push(`**Project**: ${metadata.project}`);\n if (metadata.projectId) {\n lines.push(`**Project ID**: \\`${metadata.projectId}\\``);\n }\n lines.push('');\n }\n \n // Routing Information\n if (metadata.routing) {\n lines.push('### Routing');\n lines.push('');\n lines.push(`**Destination**: ${metadata.routing.destination}`);\n lines.push(`**Confidence**: ${(metadata.routing.confidence * 100).toFixed(1)}%`);\n lines.push('');\n \n if (metadata.routing.signals.length > 0) {\n lines.push('**Classification Signals**:');\n for (const signal of metadata.routing.signals) {\n const signalType = signal.type.replace(/_/g, ' ');\n const weight = (signal.weight * 100).toFixed(0);\n lines.push(`- ${signalType}: \"${signal.value}\" (${weight}% weight)`);\n }\n lines.push('');\n }\n \n if (metadata.routing.reasoning) {\n lines.push(`**Reasoning**: ${metadata.routing.reasoning}`);\n lines.push('');\n }\n }\n \n // Tags\n if (metadata.tags && metadata.tags.length > 0) {\n lines.push('**Tags**: ' + metadata.tags.map(tag => `\\`${tag}\\``).join(', '));\n lines.push('');\n }\n \n // Duration\n if (metadata.duration) {\n lines.push(`**Duration**: ${metadata.duration}`);\n lines.push('');\n }\n \n // Separator\n lines.push('---');\n lines.push('');\n \n return lines.join('\\n');\n};\n\n/**\n * Format entity metadata as Markdown footer section\n * This goes at the END of the transcript for machine readability\n */\nexport const formatEntityMetadataMarkdown = (metadata: TranscriptMetadata): string => {\n if (!metadata.entities) {\n return '';\n }\n \n const lines: string[] = [];\n lines.push('');\n lines.push('---');\n lines.push('');\n lines.push('## Entity References');\n lines.push('');\n lines.push('<!-- Machine-readable entity metadata for indexing and querying -->');\n lines.push('');\n \n // People\n if (metadata.entities.people && metadata.entities.people.length > 0) {\n lines.push('### People');\n lines.push('');\n for (const person of metadata.entities.people) {\n lines.push(`- \\`${person.id}\\`: ${person.name}`);\n }\n lines.push('');\n }\n \n // Projects\n if (metadata.entities.projects && metadata.entities.projects.length > 0) {\n lines.push('### Projects');\n lines.push('');\n for (const project of metadata.entities.projects) {\n lines.push(`- \\`${project.id}\\`: ${project.name}`);\n }\n lines.push('');\n }\n \n // Terms\n if (metadata.entities.terms && metadata.entities.terms.length > 0) {\n lines.push('### Terms');\n lines.push('');\n for (const term of metadata.entities.terms) {\n lines.push(`- \\`${term.id}\\`: ${term.name}`);\n }\n lines.push('');\n }\n \n // Companies\n if (metadata.entities.companies && metadata.entities.companies.length > 0) {\n lines.push('### Companies');\n lines.push('');\n for (const company of metadata.entities.companies) {\n lines.push(`- \\`${company.id}\\`: ${company.name}`);\n }\n lines.push('');\n }\n \n return lines.join('\\n');\n};\n\n/**\n * Parse entity metadata from a transcript\n * Reads the Entity References section if present\n */\nexport const parseEntityMetadata = (content: string): TranscriptMetadata['entities'] | undefined => {\n // Find the Entity References section\n // Look for \"## Entity References\" and capture everything after it until the next \"##\" header or end of content\n const headerIndex = content.indexOf('## Entity References');\n if (headerIndex === -1) {\n return undefined;\n }\n \n // Find the start of the content (after the header and any whitespace/newlines)\n let contentStart = headerIndex + '## Entity References'.length;\n // Skip whitespace and newlines\n while (contentStart < content.length && (content[contentStart] === '\\n' || content[contentStart] === '\\r' || content[contentStart] === ' ' || content[contentStart] === '\\t')) {\n contentStart++;\n }\n \n // Find the end - look for next \"##\" at start of line or end of content\n const remainingContent = content.substring(contentStart);\n const nextHeaderMatch = remainingContent.match(/\\n## /);\n const sectionContent = nextHeaderMatch \n ? remainingContent.substring(0, nextHeaderMatch.index)\n : remainingContent;\n const entities: NonNullable<TranscriptMetadata['entities']> = {\n people: [],\n projects: [],\n terms: [],\n companies: [],\n };\n \n // Parse each entity type\n const parseEntities = (type: 'People' | 'Projects' | 'Terms' | 'Companies'): EntityReference[] => {\n // Map plural type names to singular entity types\n const typeMap: Record<string, 'person' | 'project' | 'term' | 'company'> = {\n 'People': 'person',\n 'Projects': 'project',\n 'Terms': 'term',\n 'Companies': 'company',\n };\n \n const entityType = typeMap[type];\n \n // Find the section for this type\n const sectionHeader = `### ${type}`;\n const sectionStart = sectionContent.indexOf(sectionHeader);\n if (sectionStart === -1) return [];\n \n // Find the end (next ### or end of content)\n // Skip past the header line (including newline)\n const headerEnd = sectionStart + sectionHeader.length;\n let sectionTextStart = headerEnd;\n // Skip whitespace and newlines after the header\n while (sectionTextStart < sectionContent.length && \n (sectionContent[sectionTextStart] === '\\n' || sectionContent[sectionTextStart] === '\\r' || sectionContent[sectionTextStart] === ' ')) {\n sectionTextStart++;\n }\n \n const afterSection = sectionContent.substring(sectionTextStart);\n const nextSection = afterSection.search(/\\n###/);\n const sectionText = nextSection === -1 ? afterSection : afterSection.substring(0, nextSection);\n \n // Extract items - match format: \"- `id`: name\"\n // Match bullet point with backticked ID and name\n const items: EntityReference[] = [];\n // Use multiline regex to match across lines, look for \"- `id`: name\" pattern\n const lines = sectionText.split('\\n');\n for (const line of lines) {\n const trimmed = line.trim();\n // Match: \"- `id`: name\" or \"- `id`:name\" (with or without space after colon)\n const match = trimmed.match(/^- `([^`]+)`:\\s*(.+)$/);\n if (match) {\n items.push({\n id: match[1],\n name: match[2].trim(),\n type: entityType,\n });\n }\n }\n \n return items;\n };\n \n entities.people = parseEntities('People');\n entities.projects = parseEntities('Projects');\n entities.terms = parseEntities('Terms');\n entities.companies = parseEntities('Companies');\n \n // Only return if we found any entities\n const hasEntities = \n entities.people.length > 0 ||\n entities.projects.length > 0 ||\n entities.terms.length > 0 ||\n entities.companies.length > 0;\n \n return hasEntities ? entities : undefined;\n};\n\n/**\n * Extract routing metadata from a RouteDecision\n */\nexport const createRoutingMetadata = (decision: Routing.RouteDecision): RoutingMetadata => {\n return {\n destination: decision.destination.path,\n confidence: decision.confidence,\n signals: decision.signals,\n reasoning: decision.reasoning,\n };\n};\n\n/**\n * Format duration in seconds to readable format (e.g., \"2m 30s\")\n */\nexport const formatDuration = (seconds: number): string => {\n const minutes = Math.floor(seconds / 60);\n const secs = Math.round(seconds % 60);\n \n if (minutes === 0) {\n return `${secs}s`;\n }\n \n if (secs === 0) {\n return `${minutes}m`;\n }\n \n return `${minutes}m ${secs}s`;\n};\n\n/**\n * Format time as HH:MM AM/PM\n */\nexport const formatTime = (date: Date): string => {\n return date.toLocaleTimeString('en-US', {\n hour: '2-digit',\n minute: '2-digit',\n hour12: true\n });\n};\n\n/**\n * Extract topic from routing signals\n */\nexport const extractTopicFromSignals = (signals: Routing.ClassificationSignal[]): string | undefined => {\n const topicSignal = signals.find(s => s.type === 'topic' || s.type === 'context_type');\n return topicSignal?.value;\n};\n\n/**\n * Extract all tags from routing signals\n * Tags are deduplicated to avoid duplicates from multiple signal sources\n */\nexport const extractTagsFromSignals = (signals: Routing.ClassificationSignal[]): string[] => {\n const tags = signals\n .filter(s => s.type !== 'context_type') // Skip generic context type\n .map(s => s.value)\n .filter((v): v is string => typeof v === 'string');\n \n // Deduplicate tags using Set\n return Array.from(new Set(tags));\n};\n\n// ============================================================================\n// Lifecycle Utilities\n// ============================================================================\n\n/**\n * Valid transcript statuses for validation\n */\nexport const VALID_STATUSES: TranscriptStatus[] = [\n 'initial', 'enhanced', 'reviewed', 'in_progress', 'closed', 'archived'\n];\n\n/**\n * Check if a string is a valid TranscriptStatus\n */\nexport const isValidStatus = (status: string): status is TranscriptStatus => {\n return VALID_STATUSES.includes(status as TranscriptStatus);\n};\n\n/**\n * Generate a unique task ID\n */\nexport const generateTaskId = (): string => {\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 8);\n return `task-${timestamp}-${random}`;\n};\n\n/**\n * Create a new task\n */\nexport const createTask = (description: string): Task => {\n return {\n id: generateTaskId(),\n description,\n status: 'open',\n created: new Date().toISOString(),\n };\n};\n\n/**\n * Update transcript status and record the transition in history\n * Returns unchanged metadata if status is the same (no duplicate history)\n */\nexport const updateStatus = (\n metadata: TranscriptMetadata,\n newStatus: TranscriptStatus\n): TranscriptMetadata => {\n const oldStatus = metadata.status;\n \n // Don't add duplicate history if status unchanged\n if (oldStatus === newStatus) {\n return metadata;\n }\n \n const transition: StatusTransition = {\n from: oldStatus || 'reviewed',\n to: newStatus,\n at: new Date().toISOString(),\n };\n \n return {\n ...metadata,\n status: newStatus,\n history: [...(metadata.history || []), transition],\n };\n};\n\n/**\n * Apply default lifecycle fields to metadata\n * Used during lazy migration of old-format transcripts\n */\nexport const applyLifecycleDefaults = (metadata: TranscriptMetadata): TranscriptMetadata => {\n return {\n ...metadata,\n status: metadata.status ?? 'initial',\n history: metadata.history ?? [],\n tasks: metadata.tasks ?? [],\n };\n};\n\n/**\n * Complete a task by ID\n */\nexport const completeTask = (metadata: TranscriptMetadata, taskId: string): TranscriptMetadata => {\n const tasks = metadata.tasks || [];\n const taskIndex = tasks.findIndex(t => t.id === taskId);\n \n if (taskIndex === -1) {\n throw new Error(`Task not found: ${taskId}`);\n }\n \n const now = new Date().toISOString();\n const updatedTasks = [...tasks];\n updatedTasks[taskIndex] = {\n ...updatedTasks[taskIndex],\n status: 'done',\n changed: now,\n completed: now,\n };\n \n return {\n ...metadata,\n tasks: updatedTasks,\n };\n};\n\n/**\n * Delete a task by ID\n */\nexport const deleteTask = (metadata: TranscriptMetadata, taskId: string): TranscriptMetadata => {\n const tasks = metadata.tasks || [];\n const taskIndex = tasks.findIndex(t => t.id === taskId);\n \n if (taskIndex === -1) {\n throw new Error(`Task not found: ${taskId}`);\n }\n \n return {\n ...metadata,\n tasks: tasks.filter(t => t.id !== taskId),\n };\n};\n\n/**\n * Add a task to metadata\n */\nexport const addTask = (metadata: TranscriptMetadata, description: string): { metadata: TranscriptMetadata; task: Task } => {\n const task = createTask(description);\n \n return {\n metadata: {\n ...metadata,\n tasks: [...(metadata.tasks || []), task],\n },\n task,\n };\n};\n\n\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index15.js","sources":["../src/util/metadata.ts"],"sourcesContent":["import * as Routing from '@/routing';\n\n// ============================================================================\n// Lifecycle Types\n// ============================================================================\n\n/**\n * Transcript lifecycle status\n * - initial: Whisper transcription complete\n * - enhanced: Context-aware enhancement complete\n * - reviewed: User has reviewed the transcript\n * - in_progress: Has outstanding tasks to complete\n * - closed: All work complete, no pending tasks\n * - archived: Archived for long-term storage\n * - deleted: Soft-delete marker (not physically removed)\n */\nexport type TranscriptStatus = 'initial' | 'enhanced' | 'reviewed' | 'in_progress' | 'closed' | 'archived' | 'deleted';\n\n/**\n * Record of a status transition with timestamp\n */\nexport interface StatusTransition {\n from: TranscriptStatus;\n to: TranscriptStatus;\n at: string; // ISO 8601 timestamp\n}\n\n/**\n * A follow-up task associated with a transcript\n */\nexport interface Task {\n id: string; // generated unique ID (e.g., task-1234567890-abc123)\n description: string;\n status: 'open' | 'done';\n created: string; // ISO 8601 timestamp\n changed?: string; // ISO 8601 timestamp - when last modified\n completed?: string; // ISO 8601 timestamp - when marked done\n}\n\n// ============================================================================\n// Entity Types\n// ============================================================================\n\nexport interface EntityReference {\n id: string;\n name: string;\n type: 'person' | 'project' | 'term' | 'company';\n}\n\nexport interface TranscriptMetadata {\n // UUID identifier\n id?: string;\n \n // Core fields\n title?: string;\n project?: string;\n projectId?: string;\n routing?: RoutingMetadata;\n tags?: string[];\n date?: Date;\n recordingTime?: string;\n confidence?: number;\n duration?: string;\n \n // Lifecycle fields\n status?: TranscriptStatus;\n history?: StatusTransition[];\n tasks?: Task[];\n \n // Entity references - entities mentioned/used in this transcript\n entities?: {\n people?: EntityReference[];\n projects?: EntityReference[];\n terms?: EntityReference[];\n companies?: EntityReference[];\n };\n}\n\nexport interface RoutingMetadata {\n destination: string;\n confidence: number;\n signals: Routing.ClassificationSignal[];\n reasoning: string;\n}\n\n/**\n * Format metadata as Markdown heading section\n */\nexport const formatMetadataMarkdown = (metadata: TranscriptMetadata): string => {\n const lines: string[] = [];\n \n // Title section\n if (metadata.title) {\n lines.push(`# ${metadata.title}`);\n lines.push('');\n }\n \n // Metadata frontmatter as readable markdown\n lines.push('## Metadata');\n lines.push('');\n \n // Date and Time\n if (metadata.date) {\n const dateStr = metadata.date.toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n });\n lines.push(`**Date**: ${dateStr}`);\n \n if (metadata.recordingTime) {\n lines.push(`**Time**: ${metadata.recordingTime}`);\n } else {\n const timeStr = metadata.date.toLocaleTimeString('en-US', {\n hour: '2-digit',\n minute: '2-digit',\n hour12: true\n });\n lines.push(`**Time**: ${timeStr}`);\n }\n }\n \n lines.push('');\n \n // Project\n if (metadata.project) {\n lines.push(`**Project**: ${metadata.project}`);\n if (metadata.projectId) {\n lines.push(`**Project ID**: \\`${metadata.projectId}\\``);\n }\n lines.push('');\n }\n \n // Routing Information\n if (metadata.routing) {\n lines.push('### Routing');\n lines.push('');\n lines.push(`**Destination**: ${metadata.routing.destination}`);\n lines.push(`**Confidence**: ${(metadata.routing.confidence * 100).toFixed(1)}%`);\n lines.push('');\n \n if (metadata.routing.signals.length > 0) {\n lines.push('**Classification Signals**:');\n for (const signal of metadata.routing.signals) {\n const signalType = signal.type.replace(/_/g, ' ');\n const weight = (signal.weight * 100).toFixed(0);\n lines.push(`- ${signalType}: \"${signal.value}\" (${weight}% weight)`);\n }\n lines.push('');\n }\n \n if (metadata.routing.reasoning) {\n lines.push(`**Reasoning**: ${metadata.routing.reasoning}`);\n lines.push('');\n }\n }\n \n // Tags\n if (metadata.tags && metadata.tags.length > 0) {\n lines.push('**Tags**: ' + metadata.tags.map(tag => `\\`${tag}\\``).join(', '));\n lines.push('');\n }\n \n // Duration\n if (metadata.duration) {\n lines.push(`**Duration**: ${metadata.duration}`);\n lines.push('');\n }\n \n // Separator\n lines.push('---');\n lines.push('');\n \n return lines.join('\\n');\n};\n\n/**\n * Format entity metadata as Markdown footer section\n * This goes at the END of the transcript for machine readability\n */\nexport const formatEntityMetadataMarkdown = (metadata: TranscriptMetadata): string => {\n if (!metadata.entities) {\n return '';\n }\n \n const lines: string[] = [];\n lines.push('');\n lines.push('---');\n lines.push('');\n lines.push('## Entity References');\n lines.push('');\n lines.push('<!-- Machine-readable entity metadata for indexing and querying -->');\n lines.push('');\n \n // People\n if (metadata.entities.people && metadata.entities.people.length > 0) {\n lines.push('### People');\n lines.push('');\n for (const person of metadata.entities.people) {\n lines.push(`- \\`${person.id}\\`: ${person.name}`);\n }\n lines.push('');\n }\n \n // Projects\n if (metadata.entities.projects && metadata.entities.projects.length > 0) {\n lines.push('### Projects');\n lines.push('');\n for (const project of metadata.entities.projects) {\n lines.push(`- \\`${project.id}\\`: ${project.name}`);\n }\n lines.push('');\n }\n \n // Terms\n if (metadata.entities.terms && metadata.entities.terms.length > 0) {\n lines.push('### Terms');\n lines.push('');\n for (const term of metadata.entities.terms) {\n lines.push(`- \\`${term.id}\\`: ${term.name}`);\n }\n lines.push('');\n }\n \n // Companies\n if (metadata.entities.companies && metadata.entities.companies.length > 0) {\n lines.push('### Companies');\n lines.push('');\n for (const company of metadata.entities.companies) {\n lines.push(`- \\`${company.id}\\`: ${company.name}`);\n }\n lines.push('');\n }\n \n return lines.join('\\n');\n};\n\n/**\n * Parse entity metadata from a transcript\n * Reads the Entity References section if present\n */\nexport const parseEntityMetadata = (content: string): TranscriptMetadata['entities'] | undefined => {\n // Find the Entity References section\n // Look for \"## Entity References\" and capture everything after it until the next \"##\" header or end of content\n const headerIndex = content.indexOf('## Entity References');\n if (headerIndex === -1) {\n return undefined;\n }\n \n // Find the start of the content (after the header and any whitespace/newlines)\n let contentStart = headerIndex + '## Entity References'.length;\n // Skip whitespace and newlines\n while (contentStart < content.length && (content[contentStart] === '\\n' || content[contentStart] === '\\r' || content[contentStart] === ' ' || content[contentStart] === '\\t')) {\n contentStart++;\n }\n \n // Find the end - look for next \"##\" at start of line or end of content\n const remainingContent = content.substring(contentStart);\n const nextHeaderMatch = remainingContent.match(/\\n## /);\n const sectionContent = nextHeaderMatch \n ? remainingContent.substring(0, nextHeaderMatch.index)\n : remainingContent;\n const entities: NonNullable<TranscriptMetadata['entities']> = {\n people: [],\n projects: [],\n terms: [],\n companies: [],\n };\n \n // Parse each entity type\n const parseEntities = (type: 'People' | 'Projects' | 'Terms' | 'Companies'): EntityReference[] => {\n // Map plural type names to singular entity types\n const typeMap: Record<string, 'person' | 'project' | 'term' | 'company'> = {\n 'People': 'person',\n 'Projects': 'project',\n 'Terms': 'term',\n 'Companies': 'company',\n };\n \n const entityType = typeMap[type];\n \n // Find the section for this type\n const sectionHeader = `### ${type}`;\n const sectionStart = sectionContent.indexOf(sectionHeader);\n if (sectionStart === -1) return [];\n \n // Find the end (next ### or end of content)\n // Skip past the header line (including newline)\n const headerEnd = sectionStart + sectionHeader.length;\n let sectionTextStart = headerEnd;\n // Skip whitespace and newlines after the header\n while (sectionTextStart < sectionContent.length && \n (sectionContent[sectionTextStart] === '\\n' || sectionContent[sectionTextStart] === '\\r' || sectionContent[sectionTextStart] === ' ')) {\n sectionTextStart++;\n }\n \n const afterSection = sectionContent.substring(sectionTextStart);\n const nextSection = afterSection.search(/\\n###/);\n const sectionText = nextSection === -1 ? afterSection : afterSection.substring(0, nextSection);\n \n // Extract items - match format: \"- `id`: name\"\n // Match bullet point with backticked ID and name\n const items: EntityReference[] = [];\n // Use multiline regex to match across lines, look for \"- `id`: name\" pattern\n const lines = sectionText.split('\\n');\n for (const line of lines) {\n const trimmed = line.trim();\n // Match: \"- `id`: name\" or \"- `id`:name\" (with or without space after colon)\n const match = trimmed.match(/^- `([^`]+)`:\\s*(.+)$/);\n if (match) {\n items.push({\n id: match[1],\n name: match[2].trim(),\n type: entityType,\n });\n }\n }\n \n return items;\n };\n \n entities.people = parseEntities('People');\n entities.projects = parseEntities('Projects');\n entities.terms = parseEntities('Terms');\n entities.companies = parseEntities('Companies');\n \n // Only return if we found any entities\n const hasEntities = \n entities.people.length > 0 ||\n entities.projects.length > 0 ||\n entities.terms.length > 0 ||\n entities.companies.length > 0;\n \n return hasEntities ? entities : undefined;\n};\n\n/**\n * Extract routing metadata from a RouteDecision\n */\nexport const createRoutingMetadata = (decision: Routing.RouteDecision): RoutingMetadata => {\n return {\n destination: decision.destination.path,\n confidence: decision.confidence,\n signals: decision.signals,\n reasoning: decision.reasoning,\n };\n};\n\n/**\n * Format duration in seconds to readable format (e.g., \"2m 30s\")\n */\nexport const formatDuration = (seconds: number): string => {\n const minutes = Math.floor(seconds / 60);\n const secs = Math.round(seconds % 60);\n \n if (minutes === 0) {\n return `${secs}s`;\n }\n \n if (secs === 0) {\n return `${minutes}m`;\n }\n \n return `${minutes}m ${secs}s`;\n};\n\n/**\n * Format time as HH:MM AM/PM\n */\nexport const formatTime = (date: Date): string => {\n return date.toLocaleTimeString('en-US', {\n hour: '2-digit',\n minute: '2-digit',\n hour12: true\n });\n};\n\n/**\n * Extract topic from routing signals\n */\nexport const extractTopicFromSignals = (signals: Routing.ClassificationSignal[]): string | undefined => {\n const topicSignal = signals.find(s => s.type === 'topic' || s.type === 'context_type');\n return topicSignal?.value;\n};\n\n/**\n * Extract all tags from routing signals\n * Tags are deduplicated to avoid duplicates from multiple signal sources\n */\nexport const extractTagsFromSignals = (signals: Routing.ClassificationSignal[]): string[] => {\n const tags = signals\n .filter(s => s.type !== 'context_type') // Skip generic context type\n .map(s => s.value)\n .filter((v): v is string => typeof v === 'string');\n \n // Deduplicate tags using Set\n return Array.from(new Set(tags));\n};\n\n// ============================================================================\n// Lifecycle Utilities\n// ============================================================================\n\n/**\n * Valid transcript statuses for validation\n */\nexport const VALID_STATUSES: TranscriptStatus[] = [\n 'initial', 'enhanced', 'reviewed', 'in_progress', 'closed', 'archived', 'deleted'\n];\n\n/**\n * Check if a string is a valid TranscriptStatus\n */\nexport const isValidStatus = (status: string): status is TranscriptStatus => {\n return VALID_STATUSES.includes(status as TranscriptStatus);\n};\n\n/**\n * Generate a unique task ID\n */\nexport const generateTaskId = (): string => {\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 8);\n return `task-${timestamp}-${random}`;\n};\n\n/**\n * Create a new task\n */\nexport const createTask = (description: string): Task => {\n return {\n id: generateTaskId(),\n description,\n status: 'open',\n created: new Date().toISOString(),\n };\n};\n\n/**\n * Update transcript status and record the transition in history\n * Returns unchanged metadata if status is the same (no duplicate history)\n */\nexport const updateStatus = (\n metadata: TranscriptMetadata,\n newStatus: TranscriptStatus\n): TranscriptMetadata => {\n const oldStatus = metadata.status;\n \n // Don't add duplicate history if status unchanged\n if (oldStatus === newStatus) {\n return metadata;\n }\n \n const transition: StatusTransition = {\n from: oldStatus || 'reviewed',\n to: newStatus,\n at: new Date().toISOString(),\n };\n \n return {\n ...metadata,\n status: newStatus,\n history: [...(metadata.history || []), transition],\n };\n};\n\n/**\n * Apply default lifecycle fields to metadata\n * Used during lazy migration of old-format transcripts\n */\nexport const applyLifecycleDefaults = (metadata: TranscriptMetadata): TranscriptMetadata => {\n return {\n ...metadata,\n status: metadata.status ?? 'initial',\n history: metadata.history ?? [],\n tasks: metadata.tasks ?? [],\n };\n};\n\n/**\n * Complete a task by ID\n */\nexport const completeTask = (metadata: TranscriptMetadata, taskId: string): TranscriptMetadata => {\n const tasks = metadata.tasks || [];\n const taskIndex = tasks.findIndex(t => t.id === taskId);\n \n if (taskIndex === -1) {\n throw new Error(`Task not found: ${taskId}`);\n }\n \n const now = new Date().toISOString();\n const updatedTasks = [...tasks];\n updatedTasks[taskIndex] = {\n ...updatedTasks[taskIndex],\n status: 'done',\n changed: now,\n completed: now,\n };\n \n return {\n ...metadata,\n tasks: updatedTasks,\n };\n};\n\n/**\n * Delete a task by ID\n */\nexport const deleteTask = (metadata: TranscriptMetadata, taskId: string): TranscriptMetadata => {\n const tasks = metadata.tasks || [];\n const taskIndex = tasks.findIndex(t => t.id === taskId);\n \n if (taskIndex === -1) {\n throw new Error(`Task not found: ${taskId}`);\n }\n \n return {\n ...metadata,\n tasks: tasks.filter(t => t.id !== taskId),\n };\n};\n\n/**\n * Add a task to metadata\n */\nexport const addTask = (metadata: TranscriptMetadata, description: string): { metadata: TranscriptMetadata; task: Task } => {\n const task = createTask(description);\n \n return {\n metadata: {\n ...metadata,\n tasks: [...(metadata.tasks || []), task],\n },\n task,\n };\n};\n\n\n"],"names":[],"mappings":"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"}
|
package/dist/index33.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Context from '@redaksjon/context';
|
|
2
2
|
import { create as create$1 } from './index6.js';
|
|
3
|
-
import { create as create$2 } from './
|
|
3
|
+
import { create as create$2 } from './index59.js';
|
|
4
4
|
import { create as create$3 } from './index11.js';
|
|
5
5
|
import { create as create$4 } from './index3.js';
|
|
6
6
|
import { create as create$5 } from './index2.js';
|
package/dist/index34.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getLogger } from './index47.js';
|
|
2
2
|
import { create as create$1 } from './index13.js';
|
|
3
3
|
import { create as create$2 } from './index14.js';
|
|
4
|
-
import { transcribeAudio } from './
|
|
5
|
-
import { stringifyJSON } from './
|
|
4
|
+
import { transcribeAudio } from './index53.js';
|
|
5
|
+
import { stringifyJSON } from './index54.js';
|
|
6
6
|
import path__default from 'node:path';
|
|
7
7
|
import { create as create$4 } from './index5.js';
|
|
8
8
|
import { create as create$3 } from './index2.js';
|
package/dist/index35.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { getLogger } from './index47.js';
|
|
2
2
|
import { create as create$1 } from './index13.js';
|
|
3
|
-
import { create as create$2 } from './
|
|
4
|
-
import { create as create$3 } from './
|
|
5
|
-
import { create as create$4 } from './
|
|
6
|
-
import { stringifyJSON } from './
|
|
3
|
+
import { create as create$2 } from './index55.js';
|
|
4
|
+
import { create as create$3 } from './index56.js';
|
|
5
|
+
import { create as create$4 } from './index57.js';
|
|
6
|
+
import { stringifyJSON } from './index54.js';
|
|
7
7
|
import path from 'path';
|
|
8
8
|
|
|
9
9
|
const create = (config, contextInstance) => {
|
package/dist/index36.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getLogger } from './index47.js';
|
|
2
2
|
import { create as create$3 } from './index14.js';
|
|
3
3
|
import { create as create$1 } from './index13.js';
|
|
4
|
-
import { create as create$2 } from './
|
|
4
|
+
import { create as create$2 } from './index58.js';
|
|
5
5
|
import { DEFAULT_INTERMEDIATE_DIRECTORY } from './index18.js';
|
|
6
6
|
import path__default from 'node:path';
|
|
7
7
|
|
package/dist/index38.js
CHANGED
|
@@ -472,9 +472,11 @@ function isValidStatusTransition(from, to) {
|
|
|
472
472
|
"enhanced": ["reviewed", "in_progress", "error"],
|
|
473
473
|
"reviewed": ["closed", "in_progress", "error"],
|
|
474
474
|
"in_progress": ["initial", "enhanced", "reviewed", "closed", "error"],
|
|
475
|
-
"closed": ["archived", "in_progress", "error"],
|
|
476
|
-
"archived": ["closed", "error"]
|
|
475
|
+
"closed": ["archived", "deleted", "in_progress", "error"],
|
|
476
|
+
"archived": ["closed", "deleted", "error"],
|
|
477
477
|
// Allow un-archiving
|
|
478
|
+
"deleted": ["archived", "closed", "error"]
|
|
479
|
+
// Allow soft-restore
|
|
478
480
|
};
|
|
479
481
|
return validTransitions[from]?.includes(to) ?? false;
|
|
480
482
|
}
|
package/dist/index38.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index38.js","sources":["../src/transcript/operations.ts"],"sourcesContent":["/**\n * Transcript Operations\n * \n * Core business logic for transcript parsing, listing, editing, and combining.\n * PKL-only implementation - all transcripts are stored in PKL format.\n */\n\nimport * as fs from 'fs/promises';\nimport * as path from 'node:path';\nimport { glob } from 'glob';\nimport * as Context from '@redaksjon/context';\nimport * as Routing from '../routing';\nimport { Project } from '@redaksjon/context';\nimport { findProjectResilient } from '../utils/entityFinder';\nimport { \n PklTranscript, \n listTranscripts as listTranscriptsFromStorage,\n} from '@redaksjon/protokoll-format';\nimport { ensurePklExtension } from './pkl-utils';\n\ntype PklMetadata = {\n id: string;\n title?: string;\n date?: Date;\n recordingTime?: string;\n project?: string;\n projectId?: string;\n tags?: string[];\n duration?: string;\n status?: TranscriptStatus;\n routing?: {\n destination?: string;\n confidence?: number;\n signals?: string[];\n reasoning?: string;\n };\n entities?: {\n people?: Array<{ id: string; name: string; type: EntityType }>;\n projects?: Array<{ id: string; name: string; type: EntityType }>;\n terms?: Array<{ id: string; name: string; type: EntityType }>;\n companies?: Array<{ id: string; name: string; type: EntityType }>;\n };\n};\n\ntype TranscriptStatus =\n | 'uploaded'\n | 'transcribing'\n | 'error'\n | 'initial'\n | 'enhanced'\n | 'reviewed'\n | 'in_progress'\n | 'closed'\n | 'archived';\n\ntype EntityType = 'person' | 'project' | 'term' | 'company';\n\n/** UUID v4 pattern — used to detect corrupted project fields */\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nfunction looksLikeUuid(value: string): boolean {\n return UUID_PATTERN.test(value);\n}\n\n/**\n * Parsed transcript structure\n */\nexport interface ParsedTranscript {\n filePath: string;\n title?: string;\n metadata: TranscriptMetadata;\n content: string;\n rawText: string;\n}\n\nexport interface TranscriptMetadata {\n date?: string;\n time?: string;\n project?: string;\n projectId?: string;\n destination?: string;\n confidence?: string;\n signals?: string[];\n reasoning?: string;\n tags?: string[];\n duration?: string;\n}\n\n/**\n * Check if input looks like a UUID (8+ hex chars)\n */\nexport function isUuidInput(input: string): boolean {\n return /^[a-f0-9]{8}/.test(input);\n}\n\n/**\n * Find transcript by UUID using glob scan\n * TODO: Replace with index-based lookup for better performance with large collections\n * \n * @param uuid - Full UUID or 8-character prefix\n * @param searchDirectories - Directories to search in\n * @returns Absolute path to transcript file, or null if not found\n */\nexport async function findTranscriptByUuid(\n uuid: string,\n searchDirectories: string[]\n): Promise<string | null> {\n const prefix = uuid.substring(0, 8); // Support both full UUID and prefix\n const pattern = `${prefix}-*.pkl`;\n \n for (const dir of searchDirectories) {\n const matches = await glob(pattern, { cwd: dir, absolute: true });\n if (matches.length > 0) {\n // Return first match - UUIDs should be unique\n return matches[0];\n }\n }\n \n return null;\n}\n\n/**\n * Parse a transcript file into its components\n * PKL-only implementation\n * \n * @param filePathOrUuid - File path or UUID to parse\n * @param searchDirectories - Optional directories to search if UUID is provided\n */\nexport const parseTranscript = async (\n filePathOrUuid: string,\n searchDirectories?: string[]\n): Promise<ParsedTranscript> => {\n let resolvedPath: string;\n \n // Check if input is a UUID\n if (isUuidInput(filePathOrUuid)) {\n if (!searchDirectories || searchDirectories.length === 0) {\n throw new Error('Search directories required for UUID lookup');\n }\n const foundPath = await findTranscriptByUuid(filePathOrUuid, searchDirectories);\n if (!foundPath) {\n throw new Error(`Transcript not found for UUID: ${filePathOrUuid}`);\n }\n resolvedPath = foundPath;\n } else {\n // Existing path-based logic\n resolvedPath = ensurePklExtension(filePathOrUuid);\n }\n \n const transcript = PklTranscript.open(resolvedPath, { readOnly: true });\n \n try {\n const pklMetadata = transcript.metadata as PklMetadata;\n const content = transcript.content;\n \n const result: ParsedTranscript = {\n filePath: resolvedPath,\n title: pklMetadata.title,\n metadata: {\n date: pklMetadata.date instanceof Date \n ? pklMetadata.date.toISOString().split('T')[0] \n : undefined,\n time: pklMetadata.recordingTime,\n project: pklMetadata.project,\n projectId: pklMetadata.projectId,\n destination: pklMetadata.routing?.destination,\n confidence: pklMetadata.routing?.confidence?.toString(),\n signals: pklMetadata.routing?.signals,\n reasoning: pklMetadata.routing?.reasoning,\n tags: pklMetadata.tags,\n duration: pklMetadata.duration,\n },\n content,\n rawText: content, // For PKL files, content is the enhanced text\n };\n \n return result;\n } finally {\n transcript.close();\n }\n};\n\n/**\n * Extract the timestamp from a transcript filename\n */\nexport const extractTimestampFromFilename = (filePath: string): { day: number; hour: number; minute: number } | null => {\n const ext = path.extname(filePath);\n const basename = path.basename(filePath, ext);\n const match = basename.match(/^(\\d{1,2})-(\\d{2})(\\d{2})/);\n \n if (match) {\n return {\n day: parseInt(match[1], 10),\n hour: parseInt(match[2], 10),\n minute: parseInt(match[3], 10),\n };\n }\n \n return null;\n};\n\n/**\n * Slugify a title for use in filenames\n */\nexport const slugifyTitle = (title: string): string => {\n return title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/--+/g, '-')\n .replace(/^-|-$/g, '')\n .slice(0, 50);\n};\n\n/**\n * Parse duration string to seconds\n */\nconst parseDuration = (duration: string): number => {\n const match = duration.match(/(\\d+):(\\d+)/);\n if (match) {\n const [, minutes, seconds] = match;\n return parseInt(minutes, 10) * 60 + parseInt(seconds, 10);\n }\n return 0;\n};\n\n/**\n * Format seconds as duration string\n */\nconst formatDuration = (seconds: number): string => {\n const minutes = Math.floor(seconds / 60);\n const secs = seconds % 60;\n return `${minutes}:${secs.toString().padStart(2, '0')}`;\n};\n\n/**\n * Expand ~ in paths\n */\nconst expandPath = (p: string): string => {\n if (p.startsWith('~')) {\n return path.join(process.env.HOME || '', p.slice(1));\n }\n return p;\n};\n\n/**\n * Extract date from metadata\n */\nconst extractDateFromMetadata = (metadata: TranscriptMetadata, filePath: string): Date => {\n if (metadata.date) {\n return new Date(metadata.date);\n }\n const timestamp = extractTimestampFromFilename(filePath);\n if (timestamp) {\n const now = new Date();\n return new Date(now.getFullYear(), now.getMonth(), timestamp.day, timestamp.hour, timestamp.minute);\n }\n return new Date();\n};\n\n/**\n * Build routing config from context and project\n */\nconst buildRoutingConfig = (\n context: Context.ContextInstance,\n _targetProject: Project\n): Routing.RoutingConfig => {\n const config = context.getConfig();\n const defaultPath = expandPath((config.outputDirectory as string) || '~/notes');\n \n const resolveRoutingPath = (routingPath: string | undefined): string => {\n if (!routingPath) {\n return defaultPath;\n }\n const expanded = expandPath(routingPath);\n if (!expanded.startsWith('/') && !expanded.match(/^[A-Za-z]:/)) {\n return path.resolve(defaultPath, expanded);\n }\n return expanded;\n };\n\n return {\n default: {\n path: resolveRoutingPath(undefined),\n structure: 'month',\n filename_options: ['date', 'time', 'subject'],\n },\n projects: context.getAllProjects()\n .filter(p => p.active !== false)\n .map(p => ({\n projectId: p.id,\n destination: {\n path: resolveRoutingPath(p.routing?.destination),\n structure: p.routing?.structure || 'month',\n filename_options: p.routing?.filename_options || ['date', 'time', 'subject'],\n },\n classification: p.classification,\n active: p.active,\n })),\n conflict_resolution: 'primary' as const,\n };\n};\n\n/**\n * Combine multiple transcripts into a single document\n * PKL-only implementation\n */\nexport const combineTranscripts = async (\n filePaths: string[],\n options: {\n projectId?: string;\n title?: string;\n dryRun?: boolean;\n verbose?: boolean;\n contextDirectory?: string;\n /** Explicit context directories (from protokoll-config.yaml) */\n contextDirectories?: string[];\n } = {}\n): Promise<{ outputPath: string; content: string }> => {\n if (filePaths.length === 0) {\n throw new Error('No transcript files provided');\n }\n \n const transcripts: ParsedTranscript[] = [];\n for (const filePath of filePaths) {\n try {\n const parsed = await parseTranscript(filePath);\n transcripts.push(parsed);\n } catch (error) {\n throw new Error(`Failed to parse transcript: ${filePath} - ${error}`);\n }\n }\n \n transcripts.sort((a, b) => {\n const aName = path.basename(a.filePath);\n const bName = path.basename(b.filePath);\n return aName.localeCompare(bName);\n });\n \n const firstTranscript = transcripts[0];\n const baseMetadata = { ...firstTranscript.metadata };\n \n // Use explicit contextDirectories from options if provided (from protokoll-config.yaml)\n const context = await Context.create({\n startingDir: options.contextDirectory || path.dirname(firstTranscript.filePath),\n contextDirectories: options.contextDirectories,\n });\n let targetProject: Project | undefined;\n \n if (options.projectId) {\n targetProject = findProjectResilient(context, options.projectId);\n baseMetadata.project = targetProject.name;\n baseMetadata.projectId = targetProject.id;\n \n if (targetProject.routing?.destination) {\n const config = context.getConfig();\n const defaultPath = expandPath((config.outputDirectory as string) || '~/notes');\n const routingPath = expandPath(targetProject.routing.destination);\n const resolvedPath = !routingPath.startsWith('/') && !routingPath.match(/^[A-Za-z]:/)\n ? path.resolve(defaultPath, routingPath)\n : routingPath;\n baseMetadata.destination = resolvedPath;\n }\n }\n \n let totalSeconds = 0;\n let hasDuration = false;\n for (const t of transcripts) {\n if (t.metadata.duration) {\n hasDuration = true;\n totalSeconds += parseDuration(t.metadata.duration);\n }\n }\n if (hasDuration && totalSeconds > 0) {\n baseMetadata.duration = formatDuration(totalSeconds);\n }\n \n const allTags = new Set<string>();\n for (const t of transcripts) {\n if (t.metadata.tags) {\n for (const tag of t.metadata.tags) {\n allTags.add(tag);\n }\n }\n }\n if (allTags.size > 0) {\n baseMetadata.tags = Array.from(allTags).sort();\n }\n \n const combinedTitle = options.title \n ? options.title\n : (firstTranscript.title \n ? `${firstTranscript.title} (Combined)`\n : 'Combined Transcript');\n \n // Build combined content\n const contentParts: string[] = [];\n for (let i = 0; i < transcripts.length; i++) {\n const t = transcripts[i];\n const sectionTitle = t.title || `Part ${i + 1}`;\n const sourceFile = path.basename(t.filePath);\n \n contentParts.push(`## ${sectionTitle}`);\n contentParts.push(`*Source: ${sourceFile}*`);\n contentParts.push('');\n contentParts.push(t.content);\n contentParts.push('');\n }\n \n const combinedContent = contentParts.join('\\n');\n \n // Determine output path\n let outputPath: string;\n \n if (targetProject?.routing?.destination) {\n const routingConfig = buildRoutingConfig(context, targetProject);\n const routing = Routing.create(routingConfig, context, undefined);\n \n const audioDate = extractDateFromMetadata(baseMetadata, firstTranscript.filePath);\n \n const routingContext: Routing.RoutingContext = {\n transcriptText: combinedContent,\n audioDate,\n sourceFile: firstTranscript.filePath,\n };\n \n const decision = routing.route(routingContext);\n outputPath = routing.buildOutputPath(decision, routingContext);\n // Ensure .pkl extension\n outputPath = outputPath.replace(/\\.md$/, '.pkl');\n } else {\n const firstDir = path.dirname(firstTranscript.filePath);\n const timestamp = extractTimestampFromFilename(firstTranscript.filePath);\n \n const filenameSuffix = options.title \n ? slugifyTitle(options.title)\n : 'combined';\n \n if (timestamp) {\n const day = timestamp.day.toString().padStart(2, '0');\n const hour = timestamp.hour.toString().padStart(2, '0');\n const minute = timestamp.minute.toString().padStart(2, '0');\n outputPath = path.join(firstDir, `${day}-${hour}${minute}-${filenameSuffix}.pkl`);\n } else {\n outputPath = path.join(firstDir, `${filenameSuffix}.pkl`);\n }\n }\n \n // Create the combined PKL transcript\n if (!options.dryRun) {\n const initialMetadata: PklMetadata = {\n id: '', // Will be auto-generated by PklTranscript.create()\n title: combinedTitle,\n date: baseMetadata.date ? new Date(baseMetadata.date) : undefined,\n recordingTime: baseMetadata.time,\n project: targetProject?.name || baseMetadata.project,\n projectId: targetProject?.id || baseMetadata.projectId,\n tags: baseMetadata.tags || [],\n duration: baseMetadata.duration,\n status: 'enhanced',\n };\n \n if (targetProject) {\n initialMetadata.entities = {\n people: [],\n projects: [{\n id: targetProject.id,\n name: targetProject.name,\n type: 'project',\n }],\n terms: [],\n companies: [],\n };\n }\n \n const newTranscript = PklTranscript.create(outputPath, initialMetadata);\n try {\n newTranscript.updateContent(combinedContent);\n } finally {\n newTranscript.close();\n }\n }\n \n return { outputPath, content: combinedContent };\n};\n\n/**\n * Edit transcript metadata and content\n * PKL-only implementation\n */\nexport const editTranscript = async (\n filePath: string,\n options: {\n title?: string;\n projectId?: string;\n tagsToAdd?: string[];\n tagsToRemove?: string[];\n dryRun?: boolean;\n verbose?: boolean;\n contextDirectory?: string;\n /** Explicit context directories (from protokoll-config.yaml) */\n contextDirectories?: string[];\n }\n): Promise<{ outputPath: string; content: string }> => {\n const pklPath = ensurePklExtension(filePath);\n const transcript = PklTranscript.open(pklPath, { readOnly: false });\n \n try {\n const pklMetadata = transcript.metadata as PklMetadata;\n const content = transcript.content;\n \n // Use explicit contextDirectories from options if provided (from protokoll-config.yaml)\n const context = await Context.create({\n startingDir: options.contextDirectory || path.dirname(pklPath),\n contextDirectories: options.contextDirectories,\n });\n let targetProject: Project | undefined;\n \n if (options.projectId) {\n targetProject = findProjectResilient(context, options.projectId);\n }\n \n const newTitle = options.title || pklMetadata.title || 'Untitled';\n \n // Build updated metadata\n const updatedMetadata: Partial<PklMetadata> = {};\n \n if (options.title) {\n updatedMetadata.title = newTitle;\n }\n \n if (targetProject) {\n updatedMetadata.project = targetProject.name;\n updatedMetadata.projectId = targetProject.id;\n \n // Update entities with the project\n const existingEntities = pklMetadata.entities || { people: [], projects: [], terms: [], companies: [] };\n updatedMetadata.entities = {\n people: existingEntities.people || [],\n projects: [{\n id: targetProject.id,\n name: targetProject.name,\n type: 'project',\n }],\n terms: existingEntities.terms || [],\n companies: existingEntities.companies || [],\n };\n }\n \n // Handle tag updates\n if (options.tagsToAdd || options.tagsToRemove) {\n const currentTags = new Set(pklMetadata.tags || []);\n \n if (options.tagsToRemove) {\n for (const tag of options.tagsToRemove) {\n currentTags.delete(tag);\n }\n }\n \n if (options.tagsToAdd) {\n for (const tag of options.tagsToAdd) {\n currentTags.add(tag);\n }\n }\n \n updatedMetadata.tags = Array.from(currentTags).sort();\n }\n \n // Determine output path\n let outputPath = pklPath;\n \n if (targetProject?.routing?.destination || options.title) {\n if (targetProject?.routing?.destination) {\n const routingConfig = buildRoutingConfig(context, targetProject);\n const routing = Routing.create(routingConfig, context, undefined);\n \n const audioDate = pklMetadata.date instanceof Date ? pklMetadata.date : new Date();\n \n const routingContext: Routing.RoutingContext = {\n transcriptText: content,\n audioDate,\n sourceFile: pklPath,\n };\n \n const decision = routing.route(routingContext);\n \n if (options.title) {\n const basePath = path.dirname(routing.buildOutputPath(decision, routingContext));\n const timestamp = extractTimestampFromFilename(pklPath);\n const sluggedTitle = slugifyTitle(options.title);\n \n if (timestamp) {\n const day = timestamp.day.toString().padStart(2, '0');\n const hour = timestamp.hour.toString().padStart(2, '0');\n const minute = timestamp.minute.toString().padStart(2, '0');\n outputPath = path.join(basePath, `${day}-${hour}${minute}-${sluggedTitle}.pkl`);\n } else {\n outputPath = path.join(basePath, `${sluggedTitle}.pkl`);\n }\n } else {\n outputPath = routing.buildOutputPath(decision, routingContext);\n outputPath = outputPath.replace(/\\.md$/, '.pkl');\n }\n } else if (options.title) {\n const dir = path.dirname(pklPath);\n const timestamp = extractTimestampFromFilename(pklPath);\n const sluggedTitle = slugifyTitle(options.title);\n \n if (timestamp) {\n const day = timestamp.day.toString().padStart(2, '0');\n const hour = timestamp.hour.toString().padStart(2, '0');\n const minute = timestamp.minute.toString().padStart(2, '0');\n outputPath = path.join(dir, `${day}-${hour}${minute}-${sluggedTitle}.pkl`);\n } else {\n outputPath = path.join(dir, `${sluggedTitle}.pkl`);\n }\n }\n }\n \n // Apply updates\n if (!options.dryRun) {\n if (Object.keys(updatedMetadata).length > 0) {\n transcript.updateMetadata(updatedMetadata);\n }\n \n // If output path changed, we need to move the file\n if (outputPath !== pklPath) {\n // Close current transcript\n transcript.close();\n \n // Create directory if needed\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n \n // Copy to new location\n await fs.copyFile(pklPath, outputPath);\n \n // Delete old file\n await fs.unlink(pklPath);\n }\n }\n \n return { outputPath, content };\n } finally {\n // Only close if not already closed (due to move operation)\n try {\n transcript.close();\n } catch {\n // Already closed\n }\n }\n};\n\n/**\n * Transcript list item\n */\nexport interface TranscriptListItem {\n path: string;\n filename: string;\n uuid: string; // UUID identifier for this transcript\n date: string;\n time?: string;\n title: string;\n hasRawTranscript: boolean;\n createdAt: Date;\n status?: TranscriptStatus;\n openTasksCount?: number;\n contentSize?: number;\n entities?: {\n people?: Array<{ id: string; name: string }>;\n projects?: Array<{ id: string; name: string }>;\n terms?: Array<{ id: string; name: string }>;\n companies?: Array<{ id: string; name: string }>;\n };\n}\n\nexport interface ListTranscriptsOptions {\n directory: string;\n limit?: number;\n offset?: number;\n sortBy?: 'date' | 'filename' | 'title';\n startDate?: string;\n endDate?: string;\n search?: string;\n projectId?: string;\n /** Project name - used as fallback when projectId is also set (matches transcripts with project name but no projectId) */\n project?: string;\n}\n\nexport interface ListTranscriptsResult {\n transcripts: TranscriptListItem[];\n total: number;\n hasMore: boolean;\n limit: number;\n offset: number;\n}\n\n/**\n * List transcripts with filtering and pagination\n * Uses the protokoll-format storage API\n */\nexport const listTranscripts = async (options: ListTranscriptsOptions): Promise<ListTranscriptsResult> => {\n const {\n directory,\n limit = 50,\n offset = 0,\n sortBy = 'date',\n startDate,\n endDate,\n search,\n projectId,\n project,\n } = options;\n \n // Use the storage API from protokoll-format\n // Pass projectId for UUID-based filtering; project (name) as fallback for transcripts without projectId\n const storageOptions = {\n directory,\n limit,\n offset,\n sortBy,\n search,\n projectId,\n project,\n startDate,\n endDate,\n };\n \n const result = await listTranscriptsFromStorage(storageOptions);\n \n // Convert storage result to operations result format\n const transcripts: TranscriptListItem[] = result.transcripts.map(item => {\n let uuid = '';\n let entities: TranscriptListItem['entities'];\n try {\n const transcript = PklTranscript.open(item.filePath, { readOnly: true });\n const meta = transcript.metadata as PklMetadata;\n uuid = meta.id;\n const mappedProjects = meta.entities?.projects?.map((e: { id: string; name: string }) => ({ id: e.id, name: e.name }));\n // If entities.projects is missing/empty but the scalar project field is set,\n // synthesise a project entry so the list view can display it correctly.\n const projectEntries = (mappedProjects && mappedProjects.length > 0)\n ? mappedProjects\n : (item.project && !looksLikeUuid(item.project))\n ? [{ id: meta.projectId || item.project, name: item.project }]\n : undefined;\n\n if (meta.entities || projectEntries) {\n entities = {\n people: meta.entities?.people?.map((e: { id: string; name: string }) => ({ id: e.id, name: e.name })),\n projects: projectEntries,\n terms: meta.entities?.terms?.map((e: { id: string; name: string }) => ({ id: e.id, name: e.name })),\n companies: meta.entities?.companies?.map((e: { id: string; name: string }) => ({ id: e.id, name: e.name })),\n };\n }\n transcript.close();\n } catch {\n uuid = '';\n if (item.project && !looksLikeUuid(item.project)) {\n entities = {\n projects: [{ id: item.project, name: item.project }],\n };\n }\n }\n \n return {\n path: item.filePath,\n filename: path.basename(item.filePath),\n uuid,\n date: item.date instanceof Date ? item.date.toISOString().split('T')[0] : '',\n time: undefined,\n title: item.title,\n hasRawTranscript: false,\n createdAt: item.date || new Date(),\n status: item.status,\n openTasksCount: undefined,\n contentSize: item.contentPreview?.length,\n entities,\n };\n });\n \n return {\n transcripts,\n total: result.total,\n hasMore: result.hasMore,\n limit,\n offset,\n };\n};\n\n/**\n * Validate status transitions for transcript lifecycle\n * \n * Ensures status changes follow valid workflow:\n * - Upload workflow: uploaded → transcribing → initial → enhanced → reviewed → closed\n * - Error can occur at any point\n * - Error status allows retry (back to uploaded or transcribing)\n * \n * @param from - Current status\n * @param to - Desired status\n * @returns true if transition is valid, false otherwise\n */\nexport function isValidStatusTransition(\n from: TranscriptStatus | undefined,\n to: TranscriptStatus\n): boolean {\n // If no current status, any status is valid (initial creation)\n if (!from) {\n return true;\n }\n \n // Define valid transitions for each status\n const validTransitions: Record<\n TranscriptStatus,\n TranscriptStatus[]\n > = {\n 'uploaded': ['transcribing', 'error'],\n 'transcribing': ['initial', 'error'],\n 'error': ['uploaded', 'transcribing'], // Allow retry\n 'initial': ['enhanced', 'in_progress', 'error'],\n 'enhanced': ['reviewed', 'in_progress', 'error'],\n 'reviewed': ['closed', 'in_progress', 'error'],\n 'in_progress': ['initial', 'enhanced', 'reviewed', 'closed', 'error'],\n 'closed': ['archived', 'in_progress', 'error'],\n 'archived': ['closed', 'error'], // Allow un-archiving\n };\n \n return validTransitions[from]?.includes(to) ?? false;\n}\n"],"names":["Routing.create","listTranscriptsFromStorage"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index38.js","sources":["../src/transcript/operations.ts"],"sourcesContent":["/**\n * Transcript Operations\n * \n * Core business logic for transcript parsing, listing, editing, and combining.\n * PKL-only implementation - all transcripts are stored in PKL format.\n */\n\nimport * as fs from 'fs/promises';\nimport * as path from 'node:path';\nimport { glob } from 'glob';\nimport * as Context from '@redaksjon/context';\nimport * as Routing from '../routing';\nimport { Project } from '@redaksjon/context';\nimport { findProjectResilient } from '../utils/entityFinder';\nimport { \n PklTranscript, \n listTranscripts as listTranscriptsFromStorage,\n} from '@redaksjon/protokoll-format';\nimport { ensurePklExtension } from './pkl-utils';\n\ntype PklMetadata = {\n id: string;\n title?: string;\n date?: Date;\n recordingTime?: string;\n project?: string;\n projectId?: string;\n tags?: string[];\n duration?: string;\n status?: TranscriptStatus;\n routing?: {\n destination?: string;\n confidence?: number;\n signals?: string[];\n reasoning?: string;\n };\n entities?: {\n people?: Array<{ id: string; name: string; type: EntityType }>;\n projects?: Array<{ id: string; name: string; type: EntityType }>;\n terms?: Array<{ id: string; name: string; type: EntityType }>;\n companies?: Array<{ id: string; name: string; type: EntityType }>;\n };\n};\n\ntype TranscriptStatus =\n | 'uploaded'\n | 'transcribing'\n | 'error'\n | 'initial'\n | 'enhanced'\n | 'reviewed'\n | 'in_progress'\n | 'closed'\n | 'archived'\n | 'deleted';\n\ntype EntityType = 'person' | 'project' | 'term' | 'company';\n\n/** UUID v4 pattern — used to detect corrupted project fields */\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nfunction looksLikeUuid(value: string): boolean {\n return UUID_PATTERN.test(value);\n}\n\n/**\n * Parsed transcript structure\n */\nexport interface ParsedTranscript {\n filePath: string;\n title?: string;\n metadata: TranscriptMetadata;\n content: string;\n rawText: string;\n}\n\nexport interface TranscriptMetadata {\n date?: string;\n time?: string;\n project?: string;\n projectId?: string;\n destination?: string;\n confidence?: string;\n signals?: string[];\n reasoning?: string;\n tags?: string[];\n duration?: string;\n}\n\n/**\n * Check if input looks like a UUID (8+ hex chars)\n */\nexport function isUuidInput(input: string): boolean {\n return /^[a-f0-9]{8}/.test(input);\n}\n\n/**\n * Find transcript by UUID using glob scan\n * TODO: Replace with index-based lookup for better performance with large collections\n * \n * @param uuid - Full UUID or 8-character prefix\n * @param searchDirectories - Directories to search in\n * @returns Absolute path to transcript file, or null if not found\n */\nexport async function findTranscriptByUuid(\n uuid: string,\n searchDirectories: string[]\n): Promise<string | null> {\n const prefix = uuid.substring(0, 8); // Support both full UUID and prefix\n const pattern = `${prefix}-*.pkl`;\n \n for (const dir of searchDirectories) {\n const matches = await glob(pattern, { cwd: dir, absolute: true });\n if (matches.length > 0) {\n // Return first match - UUIDs should be unique\n return matches[0];\n }\n }\n \n return null;\n}\n\n/**\n * Parse a transcript file into its components\n * PKL-only implementation\n * \n * @param filePathOrUuid - File path or UUID to parse\n * @param searchDirectories - Optional directories to search if UUID is provided\n */\nexport const parseTranscript = async (\n filePathOrUuid: string,\n searchDirectories?: string[]\n): Promise<ParsedTranscript> => {\n let resolvedPath: string;\n \n // Check if input is a UUID\n if (isUuidInput(filePathOrUuid)) {\n if (!searchDirectories || searchDirectories.length === 0) {\n throw new Error('Search directories required for UUID lookup');\n }\n const foundPath = await findTranscriptByUuid(filePathOrUuid, searchDirectories);\n if (!foundPath) {\n throw new Error(`Transcript not found for UUID: ${filePathOrUuid}`);\n }\n resolvedPath = foundPath;\n } else {\n // Existing path-based logic\n resolvedPath = ensurePklExtension(filePathOrUuid);\n }\n \n const transcript = PklTranscript.open(resolvedPath, { readOnly: true });\n \n try {\n const pklMetadata = transcript.metadata as PklMetadata;\n const content = transcript.content;\n \n const result: ParsedTranscript = {\n filePath: resolvedPath,\n title: pklMetadata.title,\n metadata: {\n date: pklMetadata.date instanceof Date \n ? pklMetadata.date.toISOString().split('T')[0] \n : undefined,\n time: pklMetadata.recordingTime,\n project: pklMetadata.project,\n projectId: pklMetadata.projectId,\n destination: pklMetadata.routing?.destination,\n confidence: pklMetadata.routing?.confidence?.toString(),\n signals: pklMetadata.routing?.signals,\n reasoning: pklMetadata.routing?.reasoning,\n tags: pklMetadata.tags,\n duration: pklMetadata.duration,\n },\n content,\n rawText: content, // For PKL files, content is the enhanced text\n };\n \n return result;\n } finally {\n transcript.close();\n }\n};\n\n/**\n * Extract the timestamp from a transcript filename\n */\nexport const extractTimestampFromFilename = (filePath: string): { day: number; hour: number; minute: number } | null => {\n const ext = path.extname(filePath);\n const basename = path.basename(filePath, ext);\n const match = basename.match(/^(\\d{1,2})-(\\d{2})(\\d{2})/);\n \n if (match) {\n return {\n day: parseInt(match[1], 10),\n hour: parseInt(match[2], 10),\n minute: parseInt(match[3], 10),\n };\n }\n \n return null;\n};\n\n/**\n * Slugify a title for use in filenames\n */\nexport const slugifyTitle = (title: string): string => {\n return title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/--+/g, '-')\n .replace(/^-|-$/g, '')\n .slice(0, 50);\n};\n\n/**\n * Parse duration string to seconds\n */\nconst parseDuration = (duration: string): number => {\n const match = duration.match(/(\\d+):(\\d+)/);\n if (match) {\n const [, minutes, seconds] = match;\n return parseInt(minutes, 10) * 60 + parseInt(seconds, 10);\n }\n return 0;\n};\n\n/**\n * Format seconds as duration string\n */\nconst formatDuration = (seconds: number): string => {\n const minutes = Math.floor(seconds / 60);\n const secs = seconds % 60;\n return `${minutes}:${secs.toString().padStart(2, '0')}`;\n};\n\n/**\n * Expand ~ in paths\n */\nconst expandPath = (p: string): string => {\n if (p.startsWith('~')) {\n return path.join(process.env.HOME || '', p.slice(1));\n }\n return p;\n};\n\n/**\n * Extract date from metadata\n */\nconst extractDateFromMetadata = (metadata: TranscriptMetadata, filePath: string): Date => {\n if (metadata.date) {\n return new Date(metadata.date);\n }\n const timestamp = extractTimestampFromFilename(filePath);\n if (timestamp) {\n const now = new Date();\n return new Date(now.getFullYear(), now.getMonth(), timestamp.day, timestamp.hour, timestamp.minute);\n }\n return new Date();\n};\n\n/**\n * Build routing config from context and project\n */\nconst buildRoutingConfig = (\n context: Context.ContextInstance,\n _targetProject: Project\n): Routing.RoutingConfig => {\n const config = context.getConfig();\n const defaultPath = expandPath((config.outputDirectory as string) || '~/notes');\n \n const resolveRoutingPath = (routingPath: string | undefined): string => {\n if (!routingPath) {\n return defaultPath;\n }\n const expanded = expandPath(routingPath);\n if (!expanded.startsWith('/') && !expanded.match(/^[A-Za-z]:/)) {\n return path.resolve(defaultPath, expanded);\n }\n return expanded;\n };\n\n return {\n default: {\n path: resolveRoutingPath(undefined),\n structure: 'month',\n filename_options: ['date', 'time', 'subject'],\n },\n projects: context.getAllProjects()\n .filter(p => p.active !== false)\n .map(p => ({\n projectId: p.id,\n destination: {\n path: resolveRoutingPath(p.routing?.destination),\n structure: p.routing?.structure || 'month',\n filename_options: p.routing?.filename_options || ['date', 'time', 'subject'],\n },\n classification: p.classification,\n active: p.active,\n })),\n conflict_resolution: 'primary' as const,\n };\n};\n\n/**\n * Combine multiple transcripts into a single document\n * PKL-only implementation\n */\nexport const combineTranscripts = async (\n filePaths: string[],\n options: {\n projectId?: string;\n title?: string;\n dryRun?: boolean;\n verbose?: boolean;\n contextDirectory?: string;\n /** Explicit context directories (from protokoll-config.yaml) */\n contextDirectories?: string[];\n } = {}\n): Promise<{ outputPath: string; content: string }> => {\n if (filePaths.length === 0) {\n throw new Error('No transcript files provided');\n }\n \n const transcripts: ParsedTranscript[] = [];\n for (const filePath of filePaths) {\n try {\n const parsed = await parseTranscript(filePath);\n transcripts.push(parsed);\n } catch (error) {\n throw new Error(`Failed to parse transcript: ${filePath} - ${error}`);\n }\n }\n \n transcripts.sort((a, b) => {\n const aName = path.basename(a.filePath);\n const bName = path.basename(b.filePath);\n return aName.localeCompare(bName);\n });\n \n const firstTranscript = transcripts[0];\n const baseMetadata = { ...firstTranscript.metadata };\n \n // Use explicit contextDirectories from options if provided (from protokoll-config.yaml)\n const context = await Context.create({\n startingDir: options.contextDirectory || path.dirname(firstTranscript.filePath),\n contextDirectories: options.contextDirectories,\n });\n let targetProject: Project | undefined;\n \n if (options.projectId) {\n targetProject = findProjectResilient(context, options.projectId);\n baseMetadata.project = targetProject.name;\n baseMetadata.projectId = targetProject.id;\n \n if (targetProject.routing?.destination) {\n const config = context.getConfig();\n const defaultPath = expandPath((config.outputDirectory as string) || '~/notes');\n const routingPath = expandPath(targetProject.routing.destination);\n const resolvedPath = !routingPath.startsWith('/') && !routingPath.match(/^[A-Za-z]:/)\n ? path.resolve(defaultPath, routingPath)\n : routingPath;\n baseMetadata.destination = resolvedPath;\n }\n }\n \n let totalSeconds = 0;\n let hasDuration = false;\n for (const t of transcripts) {\n if (t.metadata.duration) {\n hasDuration = true;\n totalSeconds += parseDuration(t.metadata.duration);\n }\n }\n if (hasDuration && totalSeconds > 0) {\n baseMetadata.duration = formatDuration(totalSeconds);\n }\n \n const allTags = new Set<string>();\n for (const t of transcripts) {\n if (t.metadata.tags) {\n for (const tag of t.metadata.tags) {\n allTags.add(tag);\n }\n }\n }\n if (allTags.size > 0) {\n baseMetadata.tags = Array.from(allTags).sort();\n }\n \n const combinedTitle = options.title \n ? options.title\n : (firstTranscript.title \n ? `${firstTranscript.title} (Combined)`\n : 'Combined Transcript');\n \n // Build combined content\n const contentParts: string[] = [];\n for (let i = 0; i < transcripts.length; i++) {\n const t = transcripts[i];\n const sectionTitle = t.title || `Part ${i + 1}`;\n const sourceFile = path.basename(t.filePath);\n \n contentParts.push(`## ${sectionTitle}`);\n contentParts.push(`*Source: ${sourceFile}*`);\n contentParts.push('');\n contentParts.push(t.content);\n contentParts.push('');\n }\n \n const combinedContent = contentParts.join('\\n');\n \n // Determine output path\n let outputPath: string;\n \n if (targetProject?.routing?.destination) {\n const routingConfig = buildRoutingConfig(context, targetProject);\n const routing = Routing.create(routingConfig, context, undefined);\n \n const audioDate = extractDateFromMetadata(baseMetadata, firstTranscript.filePath);\n \n const routingContext: Routing.RoutingContext = {\n transcriptText: combinedContent,\n audioDate,\n sourceFile: firstTranscript.filePath,\n };\n \n const decision = routing.route(routingContext);\n outputPath = routing.buildOutputPath(decision, routingContext);\n // Ensure .pkl extension\n outputPath = outputPath.replace(/\\.md$/, '.pkl');\n } else {\n const firstDir = path.dirname(firstTranscript.filePath);\n const timestamp = extractTimestampFromFilename(firstTranscript.filePath);\n \n const filenameSuffix = options.title \n ? slugifyTitle(options.title)\n : 'combined';\n \n if (timestamp) {\n const day = timestamp.day.toString().padStart(2, '0');\n const hour = timestamp.hour.toString().padStart(2, '0');\n const minute = timestamp.minute.toString().padStart(2, '0');\n outputPath = path.join(firstDir, `${day}-${hour}${minute}-${filenameSuffix}.pkl`);\n } else {\n outputPath = path.join(firstDir, `${filenameSuffix}.pkl`);\n }\n }\n \n // Create the combined PKL transcript\n if (!options.dryRun) {\n const initialMetadata: PklMetadata = {\n id: '', // Will be auto-generated by PklTranscript.create()\n title: combinedTitle,\n date: baseMetadata.date ? new Date(baseMetadata.date) : undefined,\n recordingTime: baseMetadata.time,\n project: targetProject?.name || baseMetadata.project,\n projectId: targetProject?.id || baseMetadata.projectId,\n tags: baseMetadata.tags || [],\n duration: baseMetadata.duration,\n status: 'enhanced',\n };\n \n if (targetProject) {\n initialMetadata.entities = {\n people: [],\n projects: [{\n id: targetProject.id,\n name: targetProject.name,\n type: 'project',\n }],\n terms: [],\n companies: [],\n };\n }\n \n // Compatibility cast: engine metadata can include newer statuses than\n // the currently installed protokoll-format type declarations.\n const newTranscript = PklTranscript.create(outputPath, initialMetadata as any);\n try {\n newTranscript.updateContent(combinedContent);\n } finally {\n newTranscript.close();\n }\n }\n \n return { outputPath, content: combinedContent };\n};\n\n/**\n * Edit transcript metadata and content\n * PKL-only implementation\n */\nexport const editTranscript = async (\n filePath: string,\n options: {\n title?: string;\n projectId?: string;\n tagsToAdd?: string[];\n tagsToRemove?: string[];\n dryRun?: boolean;\n verbose?: boolean;\n contextDirectory?: string;\n /** Explicit context directories (from protokoll-config.yaml) */\n contextDirectories?: string[];\n }\n): Promise<{ outputPath: string; content: string }> => {\n const pklPath = ensurePklExtension(filePath);\n const transcript = PklTranscript.open(pklPath, { readOnly: false });\n \n try {\n const pklMetadata = transcript.metadata as PklMetadata;\n const content = transcript.content;\n \n // Use explicit contextDirectories from options if provided (from protokoll-config.yaml)\n const context = await Context.create({\n startingDir: options.contextDirectory || path.dirname(pklPath),\n contextDirectories: options.contextDirectories,\n });\n let targetProject: Project | undefined;\n \n if (options.projectId) {\n targetProject = findProjectResilient(context, options.projectId);\n }\n \n const newTitle = options.title || pklMetadata.title || 'Untitled';\n \n // Build updated metadata\n const updatedMetadata: Partial<PklMetadata> = {};\n \n if (options.title) {\n updatedMetadata.title = newTitle;\n }\n \n if (targetProject) {\n updatedMetadata.project = targetProject.name;\n updatedMetadata.projectId = targetProject.id;\n \n // Update entities with the project\n const existingEntities = pklMetadata.entities || { people: [], projects: [], terms: [], companies: [] };\n updatedMetadata.entities = {\n people: existingEntities.people || [],\n projects: [{\n id: targetProject.id,\n name: targetProject.name,\n type: 'project',\n }],\n terms: existingEntities.terms || [],\n companies: existingEntities.companies || [],\n };\n }\n \n // Handle tag updates\n if (options.tagsToAdd || options.tagsToRemove) {\n const currentTags = new Set(pklMetadata.tags || []);\n \n if (options.tagsToRemove) {\n for (const tag of options.tagsToRemove) {\n currentTags.delete(tag);\n }\n }\n \n if (options.tagsToAdd) {\n for (const tag of options.tagsToAdd) {\n currentTags.add(tag);\n }\n }\n \n updatedMetadata.tags = Array.from(currentTags).sort();\n }\n \n // Determine output path\n let outputPath = pklPath;\n \n if (targetProject?.routing?.destination || options.title) {\n if (targetProject?.routing?.destination) {\n const routingConfig = buildRoutingConfig(context, targetProject);\n const routing = Routing.create(routingConfig, context, undefined);\n \n const audioDate = pklMetadata.date instanceof Date ? pklMetadata.date : new Date();\n \n const routingContext: Routing.RoutingContext = {\n transcriptText: content,\n audioDate,\n sourceFile: pklPath,\n };\n \n const decision = routing.route(routingContext);\n \n if (options.title) {\n const basePath = path.dirname(routing.buildOutputPath(decision, routingContext));\n const timestamp = extractTimestampFromFilename(pklPath);\n const sluggedTitle = slugifyTitle(options.title);\n \n if (timestamp) {\n const day = timestamp.day.toString().padStart(2, '0');\n const hour = timestamp.hour.toString().padStart(2, '0');\n const minute = timestamp.minute.toString().padStart(2, '0');\n outputPath = path.join(basePath, `${day}-${hour}${minute}-${sluggedTitle}.pkl`);\n } else {\n outputPath = path.join(basePath, `${sluggedTitle}.pkl`);\n }\n } else {\n outputPath = routing.buildOutputPath(decision, routingContext);\n outputPath = outputPath.replace(/\\.md$/, '.pkl');\n }\n } else if (options.title) {\n const dir = path.dirname(pklPath);\n const timestamp = extractTimestampFromFilename(pklPath);\n const sluggedTitle = slugifyTitle(options.title);\n \n if (timestamp) {\n const day = timestamp.day.toString().padStart(2, '0');\n const hour = timestamp.hour.toString().padStart(2, '0');\n const minute = timestamp.minute.toString().padStart(2, '0');\n outputPath = path.join(dir, `${day}-${hour}${minute}-${sluggedTitle}.pkl`);\n } else {\n outputPath = path.join(dir, `${sluggedTitle}.pkl`);\n }\n }\n }\n \n // Apply updates\n if (!options.dryRun) {\n if (Object.keys(updatedMetadata).length > 0) {\n // Compatibility cast: see status type drift note above.\n transcript.updateMetadata(updatedMetadata as any);\n }\n \n // If output path changed, we need to move the file\n if (outputPath !== pklPath) {\n // Close current transcript\n transcript.close();\n \n // Create directory if needed\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n \n // Copy to new location\n await fs.copyFile(pklPath, outputPath);\n \n // Delete old file\n await fs.unlink(pklPath);\n }\n }\n \n return { outputPath, content };\n } finally {\n // Only close if not already closed (due to move operation)\n try {\n transcript.close();\n } catch {\n // Already closed\n }\n }\n};\n\n/**\n * Transcript list item\n */\nexport interface TranscriptListItem {\n path: string;\n filename: string;\n uuid: string; // UUID identifier for this transcript\n date: string;\n time?: string;\n title: string;\n hasRawTranscript: boolean;\n createdAt: Date;\n status?: TranscriptStatus;\n openTasksCount?: number;\n contentSize?: number;\n entities?: {\n people?: Array<{ id: string; name: string }>;\n projects?: Array<{ id: string; name: string }>;\n terms?: Array<{ id: string; name: string }>;\n companies?: Array<{ id: string; name: string }>;\n };\n}\n\nexport interface ListTranscriptsOptions {\n directory: string;\n limit?: number;\n offset?: number;\n sortBy?: 'date' | 'filename' | 'title';\n startDate?: string;\n endDate?: string;\n search?: string;\n projectId?: string;\n /** Project name - used as fallback when projectId is also set (matches transcripts with project name but no projectId) */\n project?: string;\n}\n\nexport interface ListTranscriptsResult {\n transcripts: TranscriptListItem[];\n total: number;\n hasMore: boolean;\n limit: number;\n offset: number;\n}\n\n/**\n * List transcripts with filtering and pagination\n * Uses the protokoll-format storage API\n */\nexport const listTranscripts = async (options: ListTranscriptsOptions): Promise<ListTranscriptsResult> => {\n const {\n directory,\n limit = 50,\n offset = 0,\n sortBy = 'date',\n startDate,\n endDate,\n search,\n projectId,\n project,\n } = options;\n \n // Use the storage API from protokoll-format\n // Pass projectId for UUID-based filtering; project (name) as fallback for transcripts without projectId\n const storageOptions = {\n directory,\n limit,\n offset,\n sortBy,\n search,\n projectId,\n project,\n startDate,\n endDate,\n };\n \n const result = await listTranscriptsFromStorage(storageOptions);\n \n // Convert storage result to operations result format\n const transcripts: TranscriptListItem[] = result.transcripts.map(item => {\n let uuid = '';\n let entities: TranscriptListItem['entities'];\n try {\n const transcript = PklTranscript.open(item.filePath, { readOnly: true });\n const meta = transcript.metadata as PklMetadata;\n uuid = meta.id;\n const mappedProjects = meta.entities?.projects?.map((e: { id: string; name: string }) => ({ id: e.id, name: e.name }));\n // If entities.projects is missing/empty but the scalar project field is set,\n // synthesise a project entry so the list view can display it correctly.\n const projectEntries = (mappedProjects && mappedProjects.length > 0)\n ? mappedProjects\n : (item.project && !looksLikeUuid(item.project))\n ? [{ id: meta.projectId || item.project, name: item.project }]\n : undefined;\n\n if (meta.entities || projectEntries) {\n entities = {\n people: meta.entities?.people?.map((e: { id: string; name: string }) => ({ id: e.id, name: e.name })),\n projects: projectEntries,\n terms: meta.entities?.terms?.map((e: { id: string; name: string }) => ({ id: e.id, name: e.name })),\n companies: meta.entities?.companies?.map((e: { id: string; name: string }) => ({ id: e.id, name: e.name })),\n };\n }\n transcript.close();\n } catch {\n uuid = '';\n if (item.project && !looksLikeUuid(item.project)) {\n entities = {\n projects: [{ id: item.project, name: item.project }],\n };\n }\n }\n \n return {\n path: item.filePath,\n filename: path.basename(item.filePath),\n uuid,\n date: item.date instanceof Date ? item.date.toISOString().split('T')[0] : '',\n time: undefined,\n title: item.title,\n hasRawTranscript: false,\n createdAt: item.date || new Date(),\n status: item.status,\n openTasksCount: undefined,\n contentSize: item.contentPreview?.length,\n entities,\n };\n });\n \n return {\n transcripts,\n total: result.total,\n hasMore: result.hasMore,\n limit,\n offset,\n };\n};\n\n/**\n * Validate status transitions for transcript lifecycle\n * \n * Ensures status changes follow valid workflow:\n * - Upload workflow: uploaded → transcribing → initial → enhanced → reviewed → closed\n * - Error can occur at any point\n * - Error status allows retry (back to uploaded or transcribing)\n * \n * @param from - Current status\n * @param to - Desired status\n * @returns true if transition is valid, false otherwise\n */\nexport function isValidStatusTransition(\n from: TranscriptStatus | undefined,\n to: TranscriptStatus\n): boolean {\n // If no current status, any status is valid (initial creation)\n if (!from) {\n return true;\n }\n \n // Define valid transitions for each status\n const validTransitions: Record<\n TranscriptStatus,\n TranscriptStatus[]\n > = {\n 'uploaded': ['transcribing', 'error'],\n 'transcribing': ['initial', 'error'],\n 'error': ['uploaded', 'transcribing'], // Allow retry\n 'initial': ['enhanced', 'in_progress', 'error'],\n 'enhanced': ['reviewed', 'in_progress', 'error'],\n 'reviewed': ['closed', 'in_progress', 'error'],\n 'in_progress': ['initial', 'enhanced', 'reviewed', 'closed', 'error'],\n 'closed': ['archived', 'deleted', 'in_progress', 'error'],\n 'archived': ['closed', 'deleted', 'error'], // Allow un-archiving\n 'deleted': ['archived', 'closed', 'error'], // Allow soft-restore\n };\n \n return validTransitions[from]?.includes(to) ?? false;\n}\n"],"names":["Routing.create","listTranscriptsFromStorage"],"mappings":"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"}
|
package/dist/index41.js
CHANGED
|
@@ -27,6 +27,7 @@ async function createUploadTranscript(params) {
|
|
|
27
27
|
status: "uploaded",
|
|
28
28
|
audioFile: params.audioFile,
|
|
29
29
|
// Actual filename on disk (e.g. hash.ext) for worker to locate file
|
|
30
|
+
originalFilename: params.originalFilename,
|
|
30
31
|
audioHash: params.audioHash,
|
|
31
32
|
date: /* @__PURE__ */ new Date(),
|
|
32
33
|
title: params.title,
|
|
@@ -36,6 +37,28 @@ async function createUploadTranscript(params) {
|
|
|
36
37
|
await transcript.close();
|
|
37
38
|
return { uuid, filePath };
|
|
38
39
|
}
|
|
40
|
+
async function findTranscriptByAudioHash(audioHash, searchDirectories) {
|
|
41
|
+
for (const dir of searchDirectories) {
|
|
42
|
+
const files = await glob("**/*.pkl", { cwd: dir, absolute: true, nodir: true });
|
|
43
|
+
for (const file of files) {
|
|
44
|
+
try {
|
|
45
|
+
const transcript = PklTranscript.open(file, { readOnly: true });
|
|
46
|
+
const metadata = transcript.metadata;
|
|
47
|
+
if (metadata.audioHash === audioHash && metadata.id) {
|
|
48
|
+
await transcript.close();
|
|
49
|
+
return {
|
|
50
|
+
uuid: metadata.id,
|
|
51
|
+
filePath: file,
|
|
52
|
+
metadata
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
await transcript.close();
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
39
62
|
async function findUploadedTranscripts(searchDirectories) {
|
|
40
63
|
const results = [];
|
|
41
64
|
for (const dir of searchDirectories) {
|
|
@@ -114,5 +137,5 @@ async function markTranscriptAsFailed(filePath, errorDetails) {
|
|
|
114
137
|
await transcript.close();
|
|
115
138
|
}
|
|
116
139
|
|
|
117
|
-
export { createUploadTranscript, findTranscribingTranscripts, findUploadedTranscripts, generateFilenameWithUuid, markTranscriptAsFailed, markTranscriptAsTranscribing, resetTranscriptToUploaded };
|
|
140
|
+
export { createUploadTranscript, findTranscribingTranscripts, findTranscriptByAudioHash, findUploadedTranscripts, generateFilenameWithUuid, markTranscriptAsFailed, markTranscriptAsTranscribing, resetTranscriptToUploaded };
|
|
118
141
|
//# sourceMappingURL=index41.js.map
|
package/dist/index41.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index41.js","sources":["../src/transcript/upload-utils.ts"],"sourcesContent":["/**\n * Upload workflow utilities for audio transcription\n * \n * Handles creation of transcript records for uploaded audio files\n * and queue scanning for files awaiting transcription.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { join } from 'node:path';\nimport { glob } from 'glob';\nimport { PklTranscript } from '@redaksjon/protokoll-format';\n\ntype TranscriptMetadata = {\n id: string;\n status: 'uploaded' | 'transcribing' | 'error' | 'initial' | 'enhanced' | 'reviewed' | 'in_progress' | 'closed' | 'archived';\n audioFile?: string;\n audioHash?: string;\n date?: Date;\n title?: string;\n project?: string;\n errorDetails?: string;\n};\n\n/**\n * Parameters for creating an upload transcript\n */\nexport interface CreateUploadTranscriptParams {\n audioFile: string; // Path to uploaded audio file\n originalFilename: string; // Original uploaded filename\n audioHash: string; // File hash for deduplication\n outputDirectory: string; // Where to create PKL\n title?: string; // Optional title hint\n project?: string; // Optional project hint\n}\n\n/**\n * Generate a UUID-prefixed filename\n * Format: {8-char-uuid}-{basename}.pkl\n */\nexport function generateFilenameWithUuid(uuid: string, basename: string): string {\n // Take first 8 characters of UUID\n const prefix = uuid.substring(0, 8);\n // Remove .pkl extension from basename if present\n const base = basename.replace(/\\.pkl$/, '');\n return `${prefix}-${base}.pkl`;\n}\n\n/**\n * Format timestamp for filename\n */\nfunction formatTimestamp(date: Date): string {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const day = String(date.getDate()).padStart(2, '0');\n const hour = String(date.getHours()).padStart(2, '0');\n const minute = String(date.getMinutes()).padStart(2, '0');\n const second = String(date.getSeconds()).padStart(2, '0');\n return `${year}${month}${day}-${hour}${minute}${second}`;\n}\n\n/**\n * Create a transcript record for an uploaded audio file\n * \n * Creates a PKL file with 'uploaded' status and UUID-prefixed filename.\n * This makes the transcript discoverable via findTranscriptByUuid().\n * \n * @param params - Upload transcript parameters\n * @returns UUID and file path of created transcript\n */\nexport async function createUploadTranscript(\n params: CreateUploadTranscriptParams\n): Promise<{ uuid: string; filePath: string }> {\n const uuid = randomUUID();\n const timestamp = formatTimestamp(new Date());\n \n // Use UUID-prefixed filename for uploads\n const filename = generateFilenameWithUuid(uuid, `${timestamp}-upload.pkl`);\n const filePath = join(params.outputDirectory, filename);\n \n const metadata: TranscriptMetadata = {\n id: uuid,\n status: 'uploaded',\n audioFile: params.audioFile, // Actual filename on disk (e.g. hash.ext) for worker to locate file\n audioHash: params.audioHash,\n date: new Date(),\n title: params.title,\n project: params.project,\n };\n \n const transcript = PklTranscript.create(filePath, metadata);\n await transcript.close();\n \n return { uuid, filePath };\n}\n\n/**\n * Result from scanning for uploaded transcripts\n */\nexport interface UploadedTranscript {\n uuid: string;\n filePath: string;\n metadata: TranscriptMetadata;\n}\n\n/**\n * Find all transcripts in 'uploaded' status ready for transcription\n * \n * Scans directories for PKL files with UUID prefixes and 'uploaded' status.\n * Results are sorted by date (oldest first) for FIFO processing.\n * \n * @param searchDirectories - Directories to scan for transcripts\n * @returns Array of uploaded transcripts sorted by date\n */\nexport async function findUploadedTranscripts(\n searchDirectories: string[]\n): Promise<UploadedTranscript[]> {\n const results: UploadedTranscript[] = [];\n \n for (const dir of searchDirectories) {\n // Find all PKL files with UUID prefixes (8 hex chars followed by dash)\n const files = await glob('????????-*.pkl', { cwd: dir, absolute: true });\n \n for (const file of files) {\n try {\n const transcript = PklTranscript.open(file, { readOnly: true });\n const metadata = transcript.metadata as TranscriptMetadata;\n \n if (metadata.status === 'uploaded') {\n results.push({ \n uuid: metadata.id, \n filePath: file, \n metadata \n });\n }\n \n await transcript.close();\n } catch (error) {\n // Skip files that can't be opened (corrupted, locked, etc.)\n // eslint-disable-next-line no-console\n console.warn(`Failed to open transcript ${file}:`, error);\n }\n }\n }\n \n // Sort by date (oldest first) for FIFO processing\n return results.sort((a, b) => {\n const aTime = a.metadata.date?.getTime() || 0;\n const bTime = b.metadata.date?.getTime() || 0;\n return aTime - bTime;\n });\n}\n\n/**\n * Find transcripts in 'transcribing' status (for recovery after crash)\n * \n * These transcripts were being processed when the server stopped.\n * They should be reset to 'uploaded' and re-queued.\n * \n * @param searchDirectories - Directories to scan for transcripts\n * @returns Array of transcribing transcripts\n */\nexport async function findTranscribingTranscripts(\n searchDirectories: string[]\n): Promise<UploadedTranscript[]> {\n const results: UploadedTranscript[] = [];\n \n for (const dir of searchDirectories) {\n const files = await glob('????????-*.pkl', { cwd: dir, absolute: true });\n \n for (const file of files) {\n try {\n const transcript = PklTranscript.open(file, { readOnly: true });\n const metadata = transcript.metadata as TranscriptMetadata;\n \n if (metadata.status === 'transcribing') {\n results.push({ \n uuid: metadata.id, \n filePath: file, \n metadata \n });\n }\n \n await transcript.close();\n } catch (error) {\n // eslint-disable-next-line no-console\n console.warn(`Failed to open transcript ${file}:`, error);\n }\n }\n }\n \n return results;\n}\n\n/**\n * Reset a transcript from 'transcribing' or 'error' to 'uploaded' for retry\n * \n * Used during queue recovery on server startup or manual retry.\n * \n * @param filePath - Path to transcript file\n */\nexport async function resetTranscriptToUploaded(filePath: string): Promise<void> {\n const transcript = PklTranscript.open(filePath);\n const metadata = transcript.metadata as TranscriptMetadata;\n \n if (metadata.status === 'transcribing' || metadata.status === 'error') {\n transcript.updateMetadata({ \n status: 'uploaded',\n errorDetails: undefined, // Clear error details on retry\n });\n await transcript.close();\n } else {\n await transcript.close();\n }\n}\n\n/**\n * Mark a transcript as transcribing (in progress)\n * \n * @param filePath - Path to transcript file\n */\nexport async function markTranscriptAsTranscribing(filePath: string): Promise<void> {\n const transcript = PklTranscript.open(filePath);\n transcript.updateMetadata({ status: 'transcribing' });\n await transcript.close();\n}\n\n/**\n * Mark a transcript as failed with error details\n * \n * @param filePath - Path to transcript file\n * @param errorDetails - Error message/details\n */\nexport async function markTranscriptAsFailed(\n filePath: string, \n errorDetails: string\n): Promise<void> {\n const transcript = PklTranscript.open(filePath);\n transcript.updateMetadata({ \n status: 'error',\n errorDetails \n });\n await transcript.close();\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index41.js","sources":["../src/transcript/upload-utils.ts"],"sourcesContent":["/**\n * Upload workflow utilities for audio transcription\n * \n * Handles creation of transcript records for uploaded audio files\n * and queue scanning for files awaiting transcription.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { join } from 'node:path';\nimport { glob } from 'glob';\nimport { PklTranscript } from '@redaksjon/protokoll-format';\n\ntype TranscriptMetadata = {\n id: string;\n status: 'uploaded' | 'transcribing' | 'error' | 'initial' | 'enhanced' | 'reviewed' | 'in_progress' | 'closed' | 'archived' | 'deleted';\n audioFile?: string;\n originalFilename?: string;\n audioHash?: string;\n date?: Date;\n title?: string;\n project?: string;\n errorDetails?: string;\n};\n\n/**\n * Parameters for creating an upload transcript\n */\nexport interface CreateUploadTranscriptParams {\n audioFile: string; // Path to uploaded audio file\n originalFilename: string; // Original uploaded filename\n audioHash: string; // File hash for deduplication\n outputDirectory: string; // Where to create PKL\n title?: string; // Optional title hint\n project?: string; // Optional project hint\n}\n\n/**\n * Generate a UUID-prefixed filename\n * Format: {8-char-uuid}-{basename}.pkl\n */\nexport function generateFilenameWithUuid(uuid: string, basename: string): string {\n // Take first 8 characters of UUID\n const prefix = uuid.substring(0, 8);\n // Remove .pkl extension from basename if present\n const base = basename.replace(/\\.pkl$/, '');\n return `${prefix}-${base}.pkl`;\n}\n\n/**\n * Format timestamp for filename\n */\nfunction formatTimestamp(date: Date): string {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const day = String(date.getDate()).padStart(2, '0');\n const hour = String(date.getHours()).padStart(2, '0');\n const minute = String(date.getMinutes()).padStart(2, '0');\n const second = String(date.getSeconds()).padStart(2, '0');\n return `${year}${month}${day}-${hour}${minute}${second}`;\n}\n\n/**\n * Create a transcript record for an uploaded audio file\n * \n * Creates a PKL file with 'uploaded' status and UUID-prefixed filename.\n * This makes the transcript discoverable via findTranscriptByUuid().\n * \n * @param params - Upload transcript parameters\n * @returns UUID and file path of created transcript\n */\nexport async function createUploadTranscript(\n params: CreateUploadTranscriptParams\n): Promise<{ uuid: string; filePath: string }> {\n const uuid = randomUUID();\n const timestamp = formatTimestamp(new Date());\n \n // Use UUID-prefixed filename for uploads\n const filename = generateFilenameWithUuid(uuid, `${timestamp}-upload.pkl`);\n const filePath = join(params.outputDirectory, filename);\n \n const metadata: TranscriptMetadata = {\n id: uuid,\n status: 'uploaded',\n audioFile: params.audioFile, // Actual filename on disk (e.g. hash.ext) for worker to locate file\n originalFilename: params.originalFilename,\n audioHash: params.audioHash,\n date: new Date(),\n title: params.title,\n project: params.project,\n };\n \n // Compatibility cast: upload workflow uses extended lifecycle statuses.\n const transcript = PklTranscript.create(filePath, metadata as any);\n await transcript.close();\n \n return { uuid, filePath };\n}\n\n/**\n * Result from scanning for uploaded transcripts\n */\nexport interface UploadedTranscript {\n uuid: string;\n filePath: string;\n metadata: TranscriptMetadata;\n}\n\n/**\n * Find an existing transcript by audio hash\n *\n * Scans PKL files recursively so duplicates can still be found after\n * transcripts are moved out of the upload UUID filename format.\n */\nexport async function findTranscriptByAudioHash(\n audioHash: string,\n searchDirectories: string[]\n): Promise<UploadedTranscript | null> {\n for (const dir of searchDirectories) {\n const files = await glob('**/*.pkl', { cwd: dir, absolute: true, nodir: true });\n\n for (const file of files) {\n try {\n const transcript = PklTranscript.open(file, { readOnly: true });\n const metadata = transcript.metadata as TranscriptMetadata;\n\n if (metadata.audioHash === audioHash && metadata.id) {\n await transcript.close();\n return {\n uuid: metadata.id,\n filePath: file,\n metadata,\n };\n }\n\n await transcript.close();\n } catch {\n // Ignore unreadable/corrupt transcripts during duplicate scan.\n }\n }\n }\n\n return null;\n}\n\n/**\n * Find all transcripts in 'uploaded' status ready for transcription\n * \n * Scans directories for PKL files with UUID prefixes and 'uploaded' status.\n * Results are sorted by date (oldest first) for FIFO processing.\n * \n * @param searchDirectories - Directories to scan for transcripts\n * @returns Array of uploaded transcripts sorted by date\n */\nexport async function findUploadedTranscripts(\n searchDirectories: string[]\n): Promise<UploadedTranscript[]> {\n const results: UploadedTranscript[] = [];\n \n for (const dir of searchDirectories) {\n // Find all PKL files with UUID prefixes (8 hex chars followed by dash)\n const files = await glob('????????-*.pkl', { cwd: dir, absolute: true });\n \n for (const file of files) {\n try {\n const transcript = PklTranscript.open(file, { readOnly: true });\n const metadata = transcript.metadata as TranscriptMetadata;\n \n if (metadata.status === 'uploaded') {\n results.push({ \n uuid: metadata.id, \n filePath: file, \n metadata \n });\n }\n \n await transcript.close();\n } catch (error) {\n // Skip files that can't be opened (corrupted, locked, etc.)\n // eslint-disable-next-line no-console\n console.warn(`Failed to open transcript ${file}:`, error);\n }\n }\n }\n \n // Sort by date (oldest first) for FIFO processing\n return results.sort((a, b) => {\n const aTime = a.metadata.date?.getTime() || 0;\n const bTime = b.metadata.date?.getTime() || 0;\n return aTime - bTime;\n });\n}\n\n/**\n * Find transcripts in 'transcribing' status (for recovery after crash)\n * \n * These transcripts were being processed when the server stopped.\n * They should be reset to 'uploaded' and re-queued.\n * \n * @param searchDirectories - Directories to scan for transcripts\n * @returns Array of transcribing transcripts\n */\nexport async function findTranscribingTranscripts(\n searchDirectories: string[]\n): Promise<UploadedTranscript[]> {\n const results: UploadedTranscript[] = [];\n \n for (const dir of searchDirectories) {\n const files = await glob('????????-*.pkl', { cwd: dir, absolute: true });\n \n for (const file of files) {\n try {\n const transcript = PklTranscript.open(file, { readOnly: true });\n const metadata = transcript.metadata as TranscriptMetadata;\n \n if (metadata.status === 'transcribing') {\n results.push({ \n uuid: metadata.id, \n filePath: file, \n metadata \n });\n }\n \n await transcript.close();\n } catch (error) {\n // eslint-disable-next-line no-console\n console.warn(`Failed to open transcript ${file}:`, error);\n }\n }\n }\n \n return results;\n}\n\n/**\n * Reset a transcript from 'transcribing' or 'error' to 'uploaded' for retry\n * \n * Used during queue recovery on server startup or manual retry.\n * \n * @param filePath - Path to transcript file\n */\nexport async function resetTranscriptToUploaded(filePath: string): Promise<void> {\n const transcript = PklTranscript.open(filePath);\n const metadata = transcript.metadata as TranscriptMetadata;\n \n if (metadata.status === 'transcribing' || metadata.status === 'error') {\n transcript.updateMetadata({ \n status: 'uploaded',\n errorDetails: undefined, // Clear error details on retry\n });\n await transcript.close();\n } else {\n await transcript.close();\n }\n}\n\n/**\n * Mark a transcript as transcribing (in progress)\n * \n * @param filePath - Path to transcript file\n */\nexport async function markTranscriptAsTranscribing(filePath: string): Promise<void> {\n const transcript = PklTranscript.open(filePath);\n transcript.updateMetadata({ status: 'transcribing' });\n await transcript.close();\n}\n\n/**\n * Mark a transcript as failed with error details\n * \n * @param filePath - Path to transcript file\n * @param errorDetails - Error message/details\n */\nexport async function markTranscriptAsFailed(\n filePath: string, \n errorDetails: string\n): Promise<void> {\n const transcript = PklTranscript.open(filePath);\n transcript.updateMetadata({ \n status: 'error',\n errorDetails \n });\n await transcript.close();\n}\n"],"names":[],"mappings":"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"}
|