recording-fence.ts2.4 KBView on GitHub /**
* Parsing for the ```recording fence — a meeting recording embedded in a document.
*
* Body shape: `key=[redacted] header lines and nothing else.
*
* ```recording
* provider: fathom
* externalId: 177047966
* title: Adapt Insurance // Cedar onboarding
* at: 12:40
* recording: https://fathom.video/share/5mga_KS6XbvzmFJiRxRpzD-YFyGCMAQy
* ```
*
* `externalId` is the load-bearing key: the server resolves the provider, the media and the
* page from it, so the embed keeps working after the signed URL that was current when the
* document was written has expired. `recording:` is a fallback link only, never the source
* of playback — for Circleback it is a link that died 24 hours after ingest.
*
* See apps/server/docs/inline-meeting-video-in-documents.md.
*/
export interface ParsedRecording {
/** `crm_meeting_events.external_id`. Everything else is decoration. */
externalId: string;
/** Canonical provider id, lowercased. Display only — the server is the authority. */
provider?: string;
/** Human label for the call. */
title?: string;
/** Where to open the recording, as written (`MM:SS` / `HH:MM:SS` / seconds). */
at?: string;
/** A provider page to fall back to when nothing can be minted. */
recording?: string;
}
/**
* Parse a fence body into a recording reference, or null when it has no `externalId`.
*
* A fence with no id cannot resolve to anything, so the node renders the body raw instead —
* the same degradation the coaching fences use, and for the same reason: a visible broken
* block is debuggable, a silently empty one is not.
*/
export function parseRecording(body: string): ParsedRecording | null {
const header: Record<string, string> = {};
for (const line of body.split('\n')) {
if (!line.trim()) continue;
const idx = line.indexOf(':');
if (idx <= 0) continue;
header[line.slice(0, idx).trim().toLowerCase()] = line.slice(idx + 1).trim();
}
// `externalid` because header keys are lowercased on the way in — the agent writes
// `externalId`, which is the spelling used everywhere else in the codebase.
const externalId = header.externalid;
if (!externalId) return null;
return {
externalId,
...(header.provider ? { provider: header.provider.toLowerCase() } : {}),
...(header.title ? { title: header.title } : {}),
...(header.at ? { at: header.at } : {}),
...(header.recording ? { recording: header.recording } : {}),
};
}