@remnic/connector-fireflies 9.69.22 → 9.69.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -141,6 +141,6 @@ declare const wearableConnectorRegistration: WearableConnectorRegistration;
141
141
  * this module registers it as a side effect; calling this again is safe
142
142
  * (returns false when already registered).
143
143
  */
144
- declare function ensureFirefliesConnectorRegistered(): boolean;
144
+ declare const ensureFirefliesConnectorRegistered: () => boolean;
145
145
 
146
146
  export { FIREFLIES_DEFAULT_ENDPOINT, FIREFLIES_SOURCE_ID, FirefliesApiError, FirefliesClient, type FirefliesClientOptions, type FirefliesSentence, type FirefliesSpeaker, type FirefliesSummary, type FirefliesTranscript, TRANSCRIPTS_MAX_PAGE_SIZE, type TranscriptsPage, createFirefliesConnector, ensureFirefliesConnectorRegistered, firefliesDayWindow, resolveFirefliesApiKey, transcriptToConversation, wearableConnectorRegistration };
package/dist/index.js CHANGED
@@ -2,8 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import {
5
- registerWearableConnector,
6
- getWearableConnector
5
+ selfRegisterWearableConnector
7
6
  } from "@remnic/core";
8
7
 
9
8
  // src/client.ts
@@ -350,12 +349,7 @@ var wearableConnectorRegistration = {
350
349
  displayName: FIREFLIES_DISPLAY_NAME,
351
350
  factory: createFirefliesConnector
352
351
  };
353
- function ensureFirefliesConnectorRegistered() {
354
- if (getWearableConnector(FIREFLIES_SOURCE_ID)) return false;
355
- registerWearableConnector(wearableConnectorRegistration);
356
- return true;
357
- }
358
- ensureFirefliesConnectorRegistered();
352
+ var ensureFirefliesConnectorRegistered = selfRegisterWearableConnector(wearableConnectorRegistration);
359
353
  export {
360
354
  FIREFLIES_DEFAULT_ENDPOINT,
361
355
  FIREFLIES_SOURCE_ID,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/normalize.ts"],"sourcesContent":["/**\n * @remnic/connector-fireflies — Fireflies.ai meeting-transcript connector.\n *\n * À-la-carte optional companion of @remnic/core: installing core alone\n * never pulls this in; core discovers it at runtime via a\n * computed-specifier dynamic import (see wearables/registry.ts) or via\n * a direct import of this module, which self-registers idempotently.\n *\n * API key: `wearables.sources.fireflies.apiKey`, else the\n * `REMNIC_FIREFLIES_API_KEY` / `FIREFLIES_API_KEY` environment\n * variables (checked in that order). Create a key under\n * Settings → Developer settings in the Fireflies app.\n */\n\nimport {\n registerWearableConnector,\n getWearableConnector,\n type WearableConnectorFactoryOptions,\n type WearableConnectorRegistration,\n type WearableFetchOptions,\n type WearableFetchPage,\n type WearableSourceConnector,\n} from \"@remnic/core\";\n\nimport { FirefliesClient } from \"./client.js\";\nimport { FIREFLIES_SOURCE_ID, firefliesDayWindow, transcriptToConversation } from \"./normalize.js\";\n\nexport {\n FirefliesClient,\n FirefliesApiError,\n FIREFLIES_DEFAULT_ENDPOINT,\n TRANSCRIPTS_MAX_PAGE_SIZE,\n} from \"./client.js\";\nexport type {\n FirefliesTranscript,\n FirefliesSentence,\n FirefliesSummary,\n FirefliesSpeaker,\n TranscriptsPage,\n FirefliesClientOptions,\n} from \"./client.js\";\nexport {\n FIREFLIES_SOURCE_ID,\n firefliesDayWindow,\n transcriptToConversation,\n} from \"./normalize.js\";\n\nconst FIREFLIES_DISPLAY_NAME = \"Fireflies.ai\";\n\nexport function resolveFirefliesApiKey(\n configuredKey: string | undefined,\n env: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n if (typeof configuredKey === \"string\" && configuredKey.trim().length > 0) {\n return configuredKey.trim();\n }\n // REMNIC_* first, then the provider-conventional name.\n for (const name of [\"REMNIC_FIREFLIES_API_KEY\", \"FIREFLIES_API_KEY\"]) {\n const value = env[name];\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value.trim();\n }\n }\n return undefined;\n}\n\nexport function createFirefliesConnector(\n options: WearableConnectorFactoryOptions,\n): WearableSourceConnector {\n // Key resolution happens lazily at call time (not factory time) so\n // `wearables status` works without credentials and env changes take\n // effect without a restart; the client constructor throws the\n // actionable missing-key message.\n let client: FirefliesClient | null = null;\n const getClient = (): FirefliesClient => {\n if (!client) {\n client = new FirefliesClient({\n apiKey: resolveFirefliesApiKey(options.settings.apiKey) ?? \"\",\n baseUrl: options.settings.baseUrl,\n });\n }\n return client;\n };\n\n return {\n id: FIREFLIES_SOURCE_ID,\n displayName: FIREFLIES_DISPLAY_NAME,\n async verifyAuth(signal?: AbortSignal) {\n return getClient().verifyAuth(signal);\n },\n async fetchConversations(opts: WearableFetchOptions): Promise<WearableFetchPage> {\n const { fromDate, toDate } = firefliesDayWindow(opts.date, opts.timezone);\n const skip = parseCursor(opts.cursor);\n const page = await getClient().listTranscripts({\n fromDate,\n toDate,\n skip,\n signal: opts.signal,\n });\n return {\n conversations: page.transcripts\n .map(transcriptToConversation)\n // A transcript with no resolvable meeting date can't produce a\n // valid conversation start; drop it rather than emit an empty\n // startIso that would fail downstream parsing silently.\n .filter((conversation) => conversation.startIso !== \"\"),\n // Advance the keyset by the raw page size (not the id-filtered count)\n // while full pages keep coming, so dropped rows never truncate the day.\n nextCursor: page.hadFullPage ? String(skip + page.rawCount) : null,\n };\n },\n };\n}\n\n/** Opaque cursor is the numeric `skip` offset; anything invalid restarts at 0. */\nfunction parseCursor(cursor: string | null | undefined): number {\n if (typeof cursor !== \"string\") return 0;\n const parsed = Number(cursor);\n return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;\n}\n\nexport const wearableConnectorRegistration: WearableConnectorRegistration = {\n id: FIREFLIES_SOURCE_ID,\n displayName: FIREFLIES_DISPLAY_NAME,\n factory: createFirefliesConnector,\n};\n\n/**\n * Idempotently register the connector with the core registry. Importing\n * this module registers it as a side effect; calling this again is safe\n * (returns false when already registered).\n */\nexport function ensureFirefliesConnectorRegistered(): boolean {\n if (getWearableConnector(FIREFLIES_SOURCE_ID)) return false;\n registerWearableConnector(wearableConnectorRegistration);\n return true;\n}\n\nensureFirefliesConnectorRegistered();\n","/**\n * Minimal Fireflies.ai GraphQL API client (raw fetch, no SDK).\n *\n * API verified against https://docs.fireflies.ai/graphql-api/query/transcripts\n * and https://docs.fireflies.ai/fundamentals/authorization (fetched\n * 2026-07-21):\n * - Endpoint: POST https://api.fireflies.ai/graphql\n * - Auth: `Authorization: Bearer <api key>`\n * - `transcripts(fromDate, toDate, limit, skip)` where fromDate/toDate are\n * ISO-8601 date-time strings and `limit` maxes at 50.\n * - Each transcript exposes `id`, `title`, `date` (epoch ms), `duration`\n * (minutes), `summary { overview short_summary }`, `participants`, and\n * `sentences { index speaker_name speaker_id text start_time end_time }`\n * where start_time/end_time are second-offsets from the meeting start.\n *\n * GraphQL transport returns HTTP 200 with an `errors` array on query/auth\n * failure; those are surfaced as FirefliesApiError (a backend failure), never\n * as an empty result (AGENTS.md §22).\n */\n\nexport const FIREFLIES_DEFAULT_ENDPOINT = \"https://api.fireflies.ai/graphql\";\n\n/** Hard API maximum for `limit` on the transcripts query. */\nexport const TRANSCRIPTS_MAX_PAGE_SIZE = 50;\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst MAX_RETRIES = 3;\n/** Backoff cap so a hostile retryAfter can't stall a sync for minutes. */\nconst MAX_RETRY_DELAY_MS = 30_000;\n\nconst TRANSCRIPTS_QUERY = `query RemnicTranscripts($fromDate: DateTime, $toDate: DateTime, $limit: Int, $skip: Int) {\n transcripts(fromDate: $fromDate, toDate: $toDate, limit: $limit, skip: $skip) {\n id\n title\n date\n duration\n participants\n summary { overview short_summary }\n speakers { id name }\n sentences { index speaker_name speaker_id text start_time end_time }\n }\n}`;\n\nconst AUTH_PROBE_QUERY = `query RemnicFirefliesAuthProbe { transcripts(limit: 1) { id } }`;\n\nexport interface FirefliesSentence {\n index?: number;\n speaker_name?: string | null;\n speaker_id?: number | string | null;\n text?: string | null;\n start_time?: number | null;\n end_time?: number | null;\n}\n\nexport interface FirefliesSummary {\n overview?: string | null;\n short_summary?: string | null;\n}\n\nexport interface FirefliesSpeaker {\n id?: number | string | null;\n name?: string | null;\n}\n\nexport interface FirefliesTranscript {\n id: string;\n title?: string | null;\n /** Meeting datetime — epoch milliseconds (Float) or an ISO string. */\n date?: number | string | null;\n /** Meeting duration in minutes (Fireflies reports minutes). */\n duration?: number | null;\n participants?: string[] | null;\n summary?: FirefliesSummary | null;\n speakers?: FirefliesSpeaker[] | null;\n sentences?: FirefliesSentence[] | null;\n}\n\nexport interface TranscriptsPage {\n transcripts: FirefliesTranscript[];\n /** True when a full page came back (more may exist beyond this skip). */\n hadFullPage: boolean;\n /** Raw row count Fireflies returned at this offset (drives the skip cursor). */\n rawCount: number;\n}\n\nexport interface FirefliesClientOptions {\n apiKey: string;\n /** Override the GraphQL endpoint (self-hosted / proxy setups). */\n baseUrl?: string;\n /** Injectable for tests. Defaults to global fetch. */\n fetchImpl?: typeof fetch;\n /** Per-request timeout (ms). */\n timeoutMs?: number;\n /** Injectable sleep for tests. */\n sleep?: (ms: number) => Promise<void>;\n}\n\nexport class FirefliesApiError extends Error {\n constructor(\n message: string,\n readonly status?: number,\n ) {\n super(message);\n this.name = \"FirefliesApiError\";\n }\n}\n\n/** Narrow unknown JSON to a plain object so field reads are actually checked. */\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nexport class FirefliesClient {\n private readonly apiKey: string;\n private readonly endpoint: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly sleep: (ms: number) => Promise<void>;\n\n constructor(options: FirefliesClientOptions) {\n if (typeof options.apiKey !== \"string\" || options.apiKey.trim().length === 0) {\n throw new FirefliesApiError(\n \"Fireflies API key is missing. Set wearables.sources.fireflies.apiKey \" +\n \"or the REMNIC_FIREFLIES_API_KEY / FIREFLIES_API_KEY environment variable \" +\n \"(create a key under Settings → Developer settings in the Fireflies app).\",\n );\n }\n this.apiKey = options.apiKey.trim();\n this.endpoint = stripTrailingSlashes(options.baseUrl ?? FIREFLIES_DEFAULT_ENDPOINT);\n this.fetchImpl = options.fetchImpl ?? fetch;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.sleep = options.sleep ?? defaultSleep;\n }\n\n /** One page of transcripts created inside the [fromDate, toDate) window. */\n async listTranscripts(params: {\n fromDate: string;\n toDate: string;\n skip?: number;\n signal?: AbortSignal;\n }): Promise<TranscriptsPage> {\n const skip = Number.isInteger(params.skip) && (params.skip as number) > 0 ? (params.skip as number) : 0;\n const data = await this.graphql(\n TRANSCRIPTS_QUERY,\n { fromDate: params.fromDate, toDate: params.toDate, limit: TRANSCRIPTS_MAX_PAGE_SIZE, skip },\n params.signal,\n );\n const raw = isRecord(data) ? data.transcripts : undefined;\n if (!Array.isArray(raw)) {\n throw new FirefliesApiError(\n \"Fireflies API returned an unexpected transcripts shape (missing data.transcripts array)\",\n );\n }\n const transcripts = raw.filter(\n (entry): entry is FirefliesTranscript => isRecord(entry) && typeof entry.id === \"string\",\n );\n return { transcripts, hadFullPage: raw.length >= TRANSCRIPTS_MAX_PAGE_SIZE, rawCount: raw.length };\n }\n\n /** Cheap auth probe. */\n async verifyAuth(signal?: AbortSignal): Promise<{ ok: boolean; detail?: string }> {\n try {\n await this.graphql(AUTH_PROBE_QUERY, {}, signal);\n return { ok: true };\n } catch (err) {\n if (err instanceof FirefliesApiError && (err.status === 401 || err.status === 403)) {\n return {\n ok: false,\n detail:\n \"Fireflies rejected the API key (401/403) — create a new key under Settings → Developer settings\",\n };\n }\n return { ok: false, detail: err instanceof Error ? err.message : String(err) };\n }\n }\n\n private async graphql(\n query: string,\n variables: Record<string, unknown>,\n signal?: AbortSignal,\n ): Promise<unknown> {\n let lastError: unknown;\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n signal?.throwIfAborted();\n const timeoutSignal = AbortSignal.timeout(this.timeoutMs);\n const combined = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;\n let response: Response;\n try {\n response = await this.fetchImpl(this.endpoint, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify({ query, variables }),\n signal: combined,\n });\n } catch (err) {\n if (signal?.aborted) throw err;\n lastError = err;\n if (attempt < MAX_RETRIES) {\n await this.sleep(backoffMs(attempt));\n continue;\n }\n throw new FirefliesApiError(\n `Fireflies API request failed after ${MAX_RETRIES + 1} attempts: ${describeNetworkError(err)}`,\n );\n }\n\n if (response.status === 429 || response.status >= 500) {\n lastError = new FirefliesApiError(`Fireflies API responded ${response.status}`, response.status);\n if (attempt < MAX_RETRIES) {\n await this.sleep(await retryDelayMs(response, attempt));\n continue;\n }\n throw lastError;\n }\n if (response.status === 401 || response.status === 403) {\n throw new FirefliesApiError(`Fireflies API responded ${response.status}`, response.status);\n }\n let body: unknown;\n try {\n body = await response.json();\n } catch {\n throw new FirefliesApiError(\"Fireflies API returned a non-JSON body\");\n }\n // GraphQL returns 200 with an `errors` array on failure. That is a\n // backend failure, not an empty result (AGENTS.md §22): surface it so\n // an expired token never reads as a quiet day.\n const errors = isRecord(body) ? body.errors : undefined;\n if (Array.isArray(errors) && errors.length > 0) {\n throw graphqlError(errors[0]);\n }\n const data = isRecord(body) ? body.data : undefined;\n if (data === undefined || data === null) {\n throw new FirefliesApiError(\"Fireflies API returned no data and no errors\");\n }\n return data;\n }\n throw lastError instanceof Error ? lastError : new FirefliesApiError(\"Fireflies API request failed\");\n }\n}\n\nfunction graphqlError(first: unknown): FirefliesApiError {\n const record = isRecord(first) ? first : undefined;\n const message = record && typeof record.message === \"string\" ? record.message : \"unknown GraphQL error\";\n const extensions = record && isRecord(record.extensions) ? record.extensions : undefined;\n const code = extensions && typeof extensions.code === \"string\" ? extensions.code : undefined;\n const authLike =\n code === \"unauthenticated\" ||\n code === \"forbidden\" ||\n /unauthor|forbidden|invalid.*(token|api key)|expired/i.test(message);\n return new FirefliesApiError(`Fireflies GraphQL error: ${message}`, authLike ? 401 : undefined);\n}\n\nfunction defaultSleep(ms: number): Promise<void> {\n const { promise, resolve } = Promise.withResolvers<void>();\n setTimeout(resolve, ms);\n return promise;\n}\n\n/**\n * Network/timeout failures wrap Node error text that can carry loader\n * paths or stack fragments; sync errors reach MCP clients verbatim, so\n * only the error name + code survive.\n */\nfunction describeNetworkError(err: unknown): string {\n if (isRecord(err)) {\n const name = typeof err.name === \"string\" ? err.name : \"Error\";\n const code = err.code;\n return typeof code === \"string\" ? `${name} (${code})` : name;\n }\n return \"network error\";\n}\n\n/** Loop instead of `/\\/+$/` — CodeQL js/polynomial-redos on user-set URLs. */\nfunction stripTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value.charCodeAt(end - 1) === 47) end--;\n return value.slice(0, end);\n}\n\nfunction backoffMs(attempt: number): number {\n return Math.min(MAX_RETRY_DELAY_MS, 1_000 * 2 ** attempt);\n}\n\nasync function retryDelayMs(response: Response, attempt: number): Promise<number> {\n const header = response.headers.get(\"retry-after\");\n if (header) {\n const seconds = Number(header);\n if (Number.isFinite(seconds) && seconds >= 0) {\n return Math.min(MAX_RETRY_DELAY_MS, seconds * 1_000);\n }\n const when = Date.parse(header);\n if (Number.isFinite(when)) {\n return Math.min(MAX_RETRY_DELAY_MS, Math.max(0, when - Date.now()));\n }\n }\n return backoffMs(attempt);\n}\n","/**\n * Normalize Fireflies.ai transcripts into Remnic's provider-agnostic\n * `WearableConversation` shape, plus the timezone-aware day-window helpers\n * the Fireflies `transcripts(fromDate, toDate)` filter needs.\n *\n * Fireflies sentences carry `start_time`/`end_time` as second-offsets from the\n * meeting start (`date`, epoch ms); they are converted to absolute UTC here\n * (the same trap the Omi connector handles). Speaker turns come as\n * `speaker_name`/`speaker_id`; the wearables speaker registry owns final\n * naming, so we never guess `isWearer`. Meetings that expose a summary but no\n * transcript degrade to a single `note`-keyed segment — a note is still\n * day-anchored recall material.\n */\n\nimport type {\n WearableConversation,\n WearableTranscriptSegment,\n} from \"@remnic/core\";\n\nimport type { FirefliesTranscript } from \"./client.js\";\n\nexport const FIREFLIES_SOURCE_ID = \"fireflies\";\n\n/** \"GMT+05:30\" → \"+05:30\"; plain \"GMT\" → \"+00:00\". */\nexport function timezoneOffsetIso(instant: Date, timezone: string): string {\n try {\n const parts = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: timezone,\n timeZoneName: \"longOffset\",\n }).formatToParts(instant);\n const name = parts.find((part) => part.type === \"timeZoneName\")?.value ?? \"GMT\";\n const match = name.match(/GMT([+-]\\d{2}:\\d{2})?/);\n return match?.[1] ?? \"+00:00\";\n } catch {\n return \"+00:00\";\n }\n}\n\n/** ISO instant for local midnight of `date` in `timezone`. */\nexport function zonedDayStartIso(date: string, timezone: string): string {\n // Two-pass offset resolution: guess from midday (stable away from DST\n // transitions), then re-derive at the candidate midnight itself.\n let offset = timezoneOffsetIso(new Date(`${date}T12:00:00Z`), timezone);\n const candidate = new Date(`${date}T00:00:00${offset}`);\n const refined = timezoneOffsetIso(candidate, timezone);\n if (refined !== offset) {\n offset = refined;\n }\n return `${date}T00:00:00${offset}`;\n}\n\nexport function nextIsoDate(date: string): string {\n const parsed = new Date(`${date}T00:00:00Z`);\n parsed.setUTCDate(parsed.getUTCDate() + 1);\n return parsed.toISOString().slice(0, 10);\n}\n\n/**\n * Reject an invalid/unsupported IANA timezone loudly. `Intl.DateTimeFormat`\n * throws a RangeError for an unknown zone; without this guard a typo'd\n * timezone would silently coerce to UTC and shift every meeting's day window\n * and timestamps (AGENTS.md §39 — reject, don't silently default).\n */\nfunction assertValidTimezone(timezone: string): void {\n try {\n new Intl.DateTimeFormat(\"en-US\", { timeZone: timezone });\n } catch {\n throw new RangeError(\n `Invalid IANA timezone \"${timezone}\" for the Fireflies connector — check wearables.timezone.`,\n );\n }\n}\n\n/**\n * Half-open [fromDate, toDate) UTC ISO bounds of a local day — the window the\n * Fireflies `transcripts` query filters on.\n */\nexport function firefliesDayWindow(\n date: string,\n timezone: string,\n): { fromDate: string; toDate: string } {\n assertValidTimezone(timezone);\n return {\n fromDate: new Date(zonedDayStartIso(date, timezone)).toISOString(),\n toDate: new Date(zonedDayStartIso(nextIsoDate(date), timezone)).toISOString(),\n };\n}\n\n/** Meeting start as epoch ms, from Fireflies' epoch-ms Float or ISO string. */\nfunction meetingStartMs(date: FirefliesTranscript[\"date\"]): number {\n if (typeof date === \"number\" && Number.isFinite(date)) return date;\n if (typeof date === \"string\") {\n const parsed = Date.parse(date);\n if (Number.isFinite(parsed)) return parsed;\n }\n return Number.NaN;\n}\n\nfunction offsetToIso(startMs: number, offsetSeconds: number | null | undefined): string | undefined {\n if (Number.isNaN(startMs)) return undefined;\n if (typeof offsetSeconds !== \"number\" || !Number.isFinite(offsetSeconds)) return undefined;\n return new Date(startMs + offsetSeconds * 1_000).toISOString();\n}\n\nexport function transcriptToConversation(transcript: FirefliesTranscript): WearableConversation {\n const startMs = meetingStartMs(transcript.date);\n const startIso = Number.isNaN(startMs) ? \"\" : new Date(startMs).toISOString();\n // Fireflies reports `duration` in minutes.\n const endIso =\n !Number.isNaN(startMs) && typeof transcript.duration === \"number\" && Number.isFinite(transcript.duration)\n ? new Date(startMs + transcript.duration * 60_000).toISOString()\n : undefined;\n\n const segments: WearableTranscriptSegment[] = [];\n for (const sentence of transcript.sentences ?? []) {\n const text = typeof sentence.text === \"string\" ? sentence.text.trim() : \"\";\n if (text.length === 0) continue;\n const speakerName =\n typeof sentence.speaker_name === \"string\" && sentence.speaker_name.trim().length > 0\n ? sentence.speaker_name.trim()\n : undefined;\n const speakerId =\n sentence.speaker_id !== null && sentence.speaker_id !== undefined\n ? String(sentence.speaker_id).trim()\n : undefined;\n const segStart = offsetToIso(startMs, sentence.start_time);\n const segEnd = offsetToIso(startMs, sentence.end_time);\n segments.push({\n text,\n speakerKey: speakerName ?? (speakerId && speakerId.length > 0 ? speakerId : \"unknown\"),\n ...(speakerName !== undefined ? { speakerName } : {}),\n ...(segStart !== undefined ? { startIso: segStart } : {}),\n ...(segEnd !== undefined ? { endIso: segEnd } : {}),\n });\n }\n\n const summaryText = firstNonEmpty(transcript.summary?.overview, transcript.summary?.short_summary);\n\n // Note-only fallback: a meeting with a summary but no transcript still\n // anchors recall for the day.\n if (segments.length === 0 && summaryText !== undefined) {\n segments.push({ text: summaryText, speakerKey: \"note\" });\n }\n\n const title = typeof transcript.title === \"string\" && transcript.title.trim().length > 0\n ? transcript.title.trim()\n : undefined;\n\n return {\n id: transcript.id,\n source: FIREFLIES_SOURCE_ID,\n ...(title !== undefined ? { title } : {}),\n ...(summaryText !== undefined ? { summary: summaryText } : {}),\n startIso,\n ...(endIso !== undefined ? { endIso } : {}),\n segments,\n };\n}\n\nfunction firstNonEmpty(...values: Array<string | null | undefined>): string | undefined {\n for (const value of values) {\n if (typeof value === \"string\" && value.trim().length > 0) return value.trim();\n }\n return undefined;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/normalize.ts"],"sourcesContent":["/**\n * @remnic/connector-fireflies — Fireflies.ai meeting-transcript connector.\n *\n * À-la-carte optional companion of @remnic/core: installing core alone\n * never pulls this in; core discovers it at runtime via a\n * computed-specifier dynamic import (see wearables/registry.ts) or via\n * a direct import of this module, which self-registers idempotently.\n *\n * API key: `wearables.sources.fireflies.apiKey`, else the\n * `REMNIC_FIREFLIES_API_KEY` / `FIREFLIES_API_KEY` environment\n * variables (checked in that order). Create a key under\n * Settings → Developer settings in the Fireflies app.\n */\n\nimport {\n selfRegisterWearableConnector,\n type WearableConnectorFactoryOptions,\n type WearableConnectorRegistration,\n type WearableFetchOptions,\n type WearableFetchPage,\n type WearableSourceConnector,\n} from \"@remnic/core\";\n\nimport { FirefliesClient } from \"./client.js\";\nimport { FIREFLIES_SOURCE_ID, firefliesDayWindow, transcriptToConversation } from \"./normalize.js\";\n\nexport {\n FirefliesClient,\n FirefliesApiError,\n FIREFLIES_DEFAULT_ENDPOINT,\n TRANSCRIPTS_MAX_PAGE_SIZE,\n} from \"./client.js\";\nexport type {\n FirefliesTranscript,\n FirefliesSentence,\n FirefliesSummary,\n FirefliesSpeaker,\n TranscriptsPage,\n FirefliesClientOptions,\n} from \"./client.js\";\nexport {\n FIREFLIES_SOURCE_ID,\n firefliesDayWindow,\n transcriptToConversation,\n} from \"./normalize.js\";\n\nconst FIREFLIES_DISPLAY_NAME = \"Fireflies.ai\";\n\nexport function resolveFirefliesApiKey(\n configuredKey: string | undefined,\n env: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n if (typeof configuredKey === \"string\" && configuredKey.trim().length > 0) {\n return configuredKey.trim();\n }\n // REMNIC_* first, then the provider-conventional name.\n for (const name of [\"REMNIC_FIREFLIES_API_KEY\", \"FIREFLIES_API_KEY\"]) {\n const value = env[name];\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value.trim();\n }\n }\n return undefined;\n}\n\nexport function createFirefliesConnector(\n options: WearableConnectorFactoryOptions,\n): WearableSourceConnector {\n // Key resolution happens lazily at call time (not factory time) so\n // `wearables status` works without credentials and env changes take\n // effect without a restart; the client constructor throws the\n // actionable missing-key message.\n let client: FirefliesClient | null = null;\n const getClient = (): FirefliesClient => {\n if (!client) {\n client = new FirefliesClient({\n apiKey: resolveFirefliesApiKey(options.settings.apiKey) ?? \"\",\n baseUrl: options.settings.baseUrl,\n });\n }\n return client;\n };\n\n return {\n id: FIREFLIES_SOURCE_ID,\n displayName: FIREFLIES_DISPLAY_NAME,\n async verifyAuth(signal?: AbortSignal) {\n return getClient().verifyAuth(signal);\n },\n async fetchConversations(opts: WearableFetchOptions): Promise<WearableFetchPage> {\n const { fromDate, toDate } = firefliesDayWindow(opts.date, opts.timezone);\n const skip = parseCursor(opts.cursor);\n const page = await getClient().listTranscripts({\n fromDate,\n toDate,\n skip,\n signal: opts.signal,\n });\n return {\n conversations: page.transcripts\n .map(transcriptToConversation)\n // A transcript with no resolvable meeting date can't produce a\n // valid conversation start; drop it rather than emit an empty\n // startIso that would fail downstream parsing silently.\n .filter((conversation) => conversation.startIso !== \"\"),\n // Advance the keyset by the raw page size (not the id-filtered count)\n // while full pages keep coming, so dropped rows never truncate the day.\n nextCursor: page.hadFullPage ? String(skip + page.rawCount) : null,\n };\n },\n };\n}\n\n/** Opaque cursor is the numeric `skip` offset; anything invalid restarts at 0. */\nfunction parseCursor(cursor: string | null | undefined): number {\n if (typeof cursor !== \"string\") return 0;\n const parsed = Number(cursor);\n return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;\n}\n\nexport const wearableConnectorRegistration: WearableConnectorRegistration = {\n id: FIREFLIES_SOURCE_ID,\n displayName: FIREFLIES_DISPLAY_NAME,\n factory: createFirefliesConnector,\n};\n\n/**\n * Idempotently register the connector with the core registry. Importing\n * this module registers it as a side effect; calling this again is safe\n * (returns false when already registered).\n */\nexport const ensureFirefliesConnectorRegistered =\n selfRegisterWearableConnector(wearableConnectorRegistration);\n","/**\n * Minimal Fireflies.ai GraphQL API client (raw fetch, no SDK).\n *\n * API verified against https://docs.fireflies.ai/graphql-api/query/transcripts\n * and https://docs.fireflies.ai/fundamentals/authorization (fetched\n * 2026-07-21):\n * - Endpoint: POST https://api.fireflies.ai/graphql\n * - Auth: `Authorization: Bearer <api key>`\n * - `transcripts(fromDate, toDate, limit, skip)` where fromDate/toDate are\n * ISO-8601 date-time strings and `limit` maxes at 50.\n * - Each transcript exposes `id`, `title`, `date` (epoch ms), `duration`\n * (minutes), `summary { overview short_summary }`, `participants`, and\n * `sentences { index speaker_name speaker_id text start_time end_time }`\n * where start_time/end_time are second-offsets from the meeting start.\n *\n * GraphQL transport returns HTTP 200 with an `errors` array on query/auth\n * failure; those are surfaced as FirefliesApiError (a backend failure), never\n * as an empty result (AGENTS.md §22).\n */\n\nexport const FIREFLIES_DEFAULT_ENDPOINT = \"https://api.fireflies.ai/graphql\";\n\n/** Hard API maximum for `limit` on the transcripts query. */\nexport const TRANSCRIPTS_MAX_PAGE_SIZE = 50;\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst MAX_RETRIES = 3;\n/** Backoff cap so a hostile retryAfter can't stall a sync for minutes. */\nconst MAX_RETRY_DELAY_MS = 30_000;\n\nconst TRANSCRIPTS_QUERY = `query RemnicTranscripts($fromDate: DateTime, $toDate: DateTime, $limit: Int, $skip: Int) {\n transcripts(fromDate: $fromDate, toDate: $toDate, limit: $limit, skip: $skip) {\n id\n title\n date\n duration\n participants\n summary { overview short_summary }\n speakers { id name }\n sentences { index speaker_name speaker_id text start_time end_time }\n }\n}`;\n\nconst AUTH_PROBE_QUERY = `query RemnicFirefliesAuthProbe { transcripts(limit: 1) { id } }`;\n\nexport interface FirefliesSentence {\n index?: number;\n speaker_name?: string | null;\n speaker_id?: number | string | null;\n text?: string | null;\n start_time?: number | null;\n end_time?: number | null;\n}\n\nexport interface FirefliesSummary {\n overview?: string | null;\n short_summary?: string | null;\n}\n\nexport interface FirefliesSpeaker {\n id?: number | string | null;\n name?: string | null;\n}\n\nexport interface FirefliesTranscript {\n id: string;\n title?: string | null;\n /** Meeting datetime — epoch milliseconds (Float) or an ISO string. */\n date?: number | string | null;\n /** Meeting duration in minutes (Fireflies reports minutes). */\n duration?: number | null;\n participants?: string[] | null;\n summary?: FirefliesSummary | null;\n speakers?: FirefliesSpeaker[] | null;\n sentences?: FirefliesSentence[] | null;\n}\n\nexport interface TranscriptsPage {\n transcripts: FirefliesTranscript[];\n /** True when a full page came back (more may exist beyond this skip). */\n hadFullPage: boolean;\n /** Raw row count Fireflies returned at this offset (drives the skip cursor). */\n rawCount: number;\n}\n\nexport interface FirefliesClientOptions {\n apiKey: string;\n /** Override the GraphQL endpoint (self-hosted / proxy setups). */\n baseUrl?: string;\n /** Injectable for tests. Defaults to global fetch. */\n fetchImpl?: typeof fetch;\n /** Per-request timeout (ms). */\n timeoutMs?: number;\n /** Injectable sleep for tests. */\n sleep?: (ms: number) => Promise<void>;\n}\n\nexport class FirefliesApiError extends Error {\n constructor(\n message: string,\n readonly status?: number,\n ) {\n super(message);\n this.name = \"FirefliesApiError\";\n }\n}\n\n/** Narrow unknown JSON to a plain object so field reads are actually checked. */\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nexport class FirefliesClient {\n private readonly apiKey: string;\n private readonly endpoint: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly sleep: (ms: number) => Promise<void>;\n\n constructor(options: FirefliesClientOptions) {\n if (typeof options.apiKey !== \"string\" || options.apiKey.trim().length === 0) {\n throw new FirefliesApiError(\n \"Fireflies API key is missing. Set wearables.sources.fireflies.apiKey \" +\n \"or the REMNIC_FIREFLIES_API_KEY / FIREFLIES_API_KEY environment variable \" +\n \"(create a key under Settings → Developer settings in the Fireflies app).\",\n );\n }\n this.apiKey = options.apiKey.trim();\n this.endpoint = stripTrailingSlashes(options.baseUrl ?? FIREFLIES_DEFAULT_ENDPOINT);\n this.fetchImpl = options.fetchImpl ?? fetch;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.sleep = options.sleep ?? defaultSleep;\n }\n\n /** One page of transcripts created inside the [fromDate, toDate) window. */\n async listTranscripts(params: {\n fromDate: string;\n toDate: string;\n skip?: number;\n signal?: AbortSignal;\n }): Promise<TranscriptsPage> {\n const skip = Number.isInteger(params.skip) && (params.skip as number) > 0 ? (params.skip as number) : 0;\n const data = await this.graphql(\n TRANSCRIPTS_QUERY,\n { fromDate: params.fromDate, toDate: params.toDate, limit: TRANSCRIPTS_MAX_PAGE_SIZE, skip },\n params.signal,\n );\n const raw = isRecord(data) ? data.transcripts : undefined;\n if (!Array.isArray(raw)) {\n throw new FirefliesApiError(\n \"Fireflies API returned an unexpected transcripts shape (missing data.transcripts array)\",\n );\n }\n const transcripts = raw.filter(\n (entry): entry is FirefliesTranscript => isRecord(entry) && typeof entry.id === \"string\",\n );\n return { transcripts, hadFullPage: raw.length >= TRANSCRIPTS_MAX_PAGE_SIZE, rawCount: raw.length };\n }\n\n /** Cheap auth probe. */\n async verifyAuth(signal?: AbortSignal): Promise<{ ok: boolean; detail?: string }> {\n try {\n await this.graphql(AUTH_PROBE_QUERY, {}, signal);\n return { ok: true };\n } catch (err) {\n if (err instanceof FirefliesApiError && (err.status === 401 || err.status === 403)) {\n return {\n ok: false,\n detail:\n \"Fireflies rejected the API key (401/403) — create a new key under Settings → Developer settings\",\n };\n }\n return { ok: false, detail: err instanceof Error ? err.message : String(err) };\n }\n }\n\n private async graphql(\n query: string,\n variables: Record<string, unknown>,\n signal?: AbortSignal,\n ): Promise<unknown> {\n let lastError: unknown;\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n signal?.throwIfAborted();\n const timeoutSignal = AbortSignal.timeout(this.timeoutMs);\n const combined = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;\n let response: Response;\n try {\n response = await this.fetchImpl(this.endpoint, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify({ query, variables }),\n signal: combined,\n });\n } catch (err) {\n if (signal?.aborted) throw err;\n lastError = err;\n if (attempt < MAX_RETRIES) {\n await this.sleep(backoffMs(attempt));\n continue;\n }\n throw new FirefliesApiError(\n `Fireflies API request failed after ${MAX_RETRIES + 1} attempts: ${describeNetworkError(err)}`,\n );\n }\n\n if (response.status === 429 || response.status >= 500) {\n lastError = new FirefliesApiError(`Fireflies API responded ${response.status}`, response.status);\n if (attempt < MAX_RETRIES) {\n await this.sleep(await retryDelayMs(response, attempt));\n continue;\n }\n throw lastError;\n }\n if (response.status === 401 || response.status === 403) {\n throw new FirefliesApiError(`Fireflies API responded ${response.status}`, response.status);\n }\n let body: unknown;\n try {\n body = await response.json();\n } catch {\n throw new FirefliesApiError(\"Fireflies API returned a non-JSON body\");\n }\n // GraphQL returns 200 with an `errors` array on failure. That is a\n // backend failure, not an empty result (AGENTS.md §22): surface it so\n // an expired token never reads as a quiet day.\n const errors = isRecord(body) ? body.errors : undefined;\n if (Array.isArray(errors) && errors.length > 0) {\n throw graphqlError(errors[0]);\n }\n const data = isRecord(body) ? body.data : undefined;\n if (data === undefined || data === null) {\n throw new FirefliesApiError(\"Fireflies API returned no data and no errors\");\n }\n return data;\n }\n throw lastError instanceof Error ? lastError : new FirefliesApiError(\"Fireflies API request failed\");\n }\n}\n\nfunction graphqlError(first: unknown): FirefliesApiError {\n const record = isRecord(first) ? first : undefined;\n const message = record && typeof record.message === \"string\" ? record.message : \"unknown GraphQL error\";\n const extensions = record && isRecord(record.extensions) ? record.extensions : undefined;\n const code = extensions && typeof extensions.code === \"string\" ? extensions.code : undefined;\n const authLike =\n code === \"unauthenticated\" ||\n code === \"forbidden\" ||\n /unauthor|forbidden|invalid.*(token|api key)|expired/i.test(message);\n return new FirefliesApiError(`Fireflies GraphQL error: ${message}`, authLike ? 401 : undefined);\n}\n\nfunction defaultSleep(ms: number): Promise<void> {\n const { promise, resolve } = Promise.withResolvers<void>();\n setTimeout(resolve, ms);\n return promise;\n}\n\n/**\n * Network/timeout failures wrap Node error text that can carry loader\n * paths or stack fragments; sync errors reach MCP clients verbatim, so\n * only the error name + code survive.\n */\nfunction describeNetworkError(err: unknown): string {\n if (isRecord(err)) {\n const name = typeof err.name === \"string\" ? err.name : \"Error\";\n const code = err.code;\n return typeof code === \"string\" ? `${name} (${code})` : name;\n }\n return \"network error\";\n}\n\n/** Loop instead of `/\\/+$/` — CodeQL js/polynomial-redos on user-set URLs. */\nfunction stripTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value.charCodeAt(end - 1) === 47) end--;\n return value.slice(0, end);\n}\n\nfunction backoffMs(attempt: number): number {\n return Math.min(MAX_RETRY_DELAY_MS, 1_000 * 2 ** attempt);\n}\n\nasync function retryDelayMs(response: Response, attempt: number): Promise<number> {\n const header = response.headers.get(\"retry-after\");\n if (header) {\n const seconds = Number(header);\n if (Number.isFinite(seconds) && seconds >= 0) {\n return Math.min(MAX_RETRY_DELAY_MS, seconds * 1_000);\n }\n const when = Date.parse(header);\n if (Number.isFinite(when)) {\n return Math.min(MAX_RETRY_DELAY_MS, Math.max(0, when - Date.now()));\n }\n }\n return backoffMs(attempt);\n}\n","/**\n * Normalize Fireflies.ai transcripts into Remnic's provider-agnostic\n * `WearableConversation` shape, plus the timezone-aware day-window helpers\n * the Fireflies `transcripts(fromDate, toDate)` filter needs.\n *\n * Fireflies sentences carry `start_time`/`end_time` as second-offsets from the\n * meeting start (`date`, epoch ms); they are converted to absolute UTC here\n * (the same trap the Omi connector handles). Speaker turns come as\n * `speaker_name`/`speaker_id`; the wearables speaker registry owns final\n * naming, so we never guess `isWearer`. Meetings that expose a summary but no\n * transcript degrade to a single `note`-keyed segment — a note is still\n * day-anchored recall material.\n */\n\nimport type {\n WearableConversation,\n WearableTranscriptSegment,\n} from \"@remnic/core\";\n\nimport type { FirefliesTranscript } from \"./client.js\";\n\nexport const FIREFLIES_SOURCE_ID = \"fireflies\";\n\n/** \"GMT+05:30\" → \"+05:30\"; plain \"GMT\" → \"+00:00\". */\nexport function timezoneOffsetIso(instant: Date, timezone: string): string {\n try {\n const parts = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: timezone,\n timeZoneName: \"longOffset\",\n }).formatToParts(instant);\n const name = parts.find((part) => part.type === \"timeZoneName\")?.value ?? \"GMT\";\n const match = name.match(/GMT([+-]\\d{2}:\\d{2})?/);\n return match?.[1] ?? \"+00:00\";\n } catch {\n return \"+00:00\";\n }\n}\n\n/** ISO instant for local midnight of `date` in `timezone`. */\nexport function zonedDayStartIso(date: string, timezone: string): string {\n // Two-pass offset resolution: guess from midday (stable away from DST\n // transitions), then re-derive at the candidate midnight itself.\n let offset = timezoneOffsetIso(new Date(`${date}T12:00:00Z`), timezone);\n const candidate = new Date(`${date}T00:00:00${offset}`);\n const refined = timezoneOffsetIso(candidate, timezone);\n if (refined !== offset) {\n offset = refined;\n }\n return `${date}T00:00:00${offset}`;\n}\n\nexport function nextIsoDate(date: string): string {\n const parsed = new Date(`${date}T00:00:00Z`);\n parsed.setUTCDate(parsed.getUTCDate() + 1);\n return parsed.toISOString().slice(0, 10);\n}\n\n/**\n * Reject an invalid/unsupported IANA timezone loudly. `Intl.DateTimeFormat`\n * throws a RangeError for an unknown zone; without this guard a typo'd\n * timezone would silently coerce to UTC and shift every meeting's day window\n * and timestamps (AGENTS.md §39 — reject, don't silently default).\n */\nfunction assertValidTimezone(timezone: string): void {\n try {\n new Intl.DateTimeFormat(\"en-US\", { timeZone: timezone });\n } catch {\n throw new RangeError(\n `Invalid IANA timezone \"${timezone}\" for the Fireflies connector — check wearables.timezone.`,\n );\n }\n}\n\n/**\n * Half-open [fromDate, toDate) UTC ISO bounds of a local day — the window the\n * Fireflies `transcripts` query filters on.\n */\nexport function firefliesDayWindow(\n date: string,\n timezone: string,\n): { fromDate: string; toDate: string } {\n assertValidTimezone(timezone);\n return {\n fromDate: new Date(zonedDayStartIso(date, timezone)).toISOString(),\n toDate: new Date(zonedDayStartIso(nextIsoDate(date), timezone)).toISOString(),\n };\n}\n\n/** Meeting start as epoch ms, from Fireflies' epoch-ms Float or ISO string. */\nfunction meetingStartMs(date: FirefliesTranscript[\"date\"]): number {\n if (typeof date === \"number\" && Number.isFinite(date)) return date;\n if (typeof date === \"string\") {\n const parsed = Date.parse(date);\n if (Number.isFinite(parsed)) return parsed;\n }\n return Number.NaN;\n}\n\nfunction offsetToIso(startMs: number, offsetSeconds: number | null | undefined): string | undefined {\n if (Number.isNaN(startMs)) return undefined;\n if (typeof offsetSeconds !== \"number\" || !Number.isFinite(offsetSeconds)) return undefined;\n return new Date(startMs + offsetSeconds * 1_000).toISOString();\n}\n\nexport function transcriptToConversation(transcript: FirefliesTranscript): WearableConversation {\n const startMs = meetingStartMs(transcript.date);\n const startIso = Number.isNaN(startMs) ? \"\" : new Date(startMs).toISOString();\n // Fireflies reports `duration` in minutes.\n const endIso =\n !Number.isNaN(startMs) && typeof transcript.duration === \"number\" && Number.isFinite(transcript.duration)\n ? new Date(startMs + transcript.duration * 60_000).toISOString()\n : undefined;\n\n const segments: WearableTranscriptSegment[] = [];\n for (const sentence of transcript.sentences ?? []) {\n const text = typeof sentence.text === \"string\" ? sentence.text.trim() : \"\";\n if (text.length === 0) continue;\n const speakerName =\n typeof sentence.speaker_name === \"string\" && sentence.speaker_name.trim().length > 0\n ? sentence.speaker_name.trim()\n : undefined;\n const speakerId =\n sentence.speaker_id !== null && sentence.speaker_id !== undefined\n ? String(sentence.speaker_id).trim()\n : undefined;\n const segStart = offsetToIso(startMs, sentence.start_time);\n const segEnd = offsetToIso(startMs, sentence.end_time);\n segments.push({\n text,\n speakerKey: speakerName ?? (speakerId && speakerId.length > 0 ? speakerId : \"unknown\"),\n ...(speakerName !== undefined ? { speakerName } : {}),\n ...(segStart !== undefined ? { startIso: segStart } : {}),\n ...(segEnd !== undefined ? { endIso: segEnd } : {}),\n });\n }\n\n const summaryText = firstNonEmpty(transcript.summary?.overview, transcript.summary?.short_summary);\n\n // Note-only fallback: a meeting with a summary but no transcript still\n // anchors recall for the day.\n if (segments.length === 0 && summaryText !== undefined) {\n segments.push({ text: summaryText, speakerKey: \"note\" });\n }\n\n const title = typeof transcript.title === \"string\" && transcript.title.trim().length > 0\n ? transcript.title.trim()\n : undefined;\n\n return {\n id: transcript.id,\n source: FIREFLIES_SOURCE_ID,\n ...(title !== undefined ? { title } : {}),\n ...(summaryText !== undefined ? { summary: summaryText } : {}),\n startIso,\n ...(endIso !== undefined ? { endIso } : {}),\n segments,\n };\n}\n\nfunction firstNonEmpty(...values: Array<string | null | undefined>): string | undefined {\n for (const value of values) {\n if (typeof value === \"string\" && value.trim().length > 0) return value.trim();\n }\n return undefined;\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/connector-fireflies",
3
- "version": "9.69.22",
3
+ "version": "9.69.24",
4
4
  "description": "Fireflies.ai meeting-transcript connector for Remnic — pull, clean, and remember meeting transcripts",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -19,13 +19,13 @@
19
19
  "provenance": true
20
20
  },
21
21
  "peerDependencies": {
22
- "@remnic/core": "^9.69.22"
22
+ "@remnic/core": "^9.69.24"
23
23
  },
24
24
  "devDependencies": {
25
25
  "tsup": "^8.0.0",
26
26
  "typescript": "^5.7.0",
27
27
  "tsx": "^4.0.0",
28
- "@remnic/core": "9.69.22"
28
+ "@remnic/core": "9.69.24"
29
29
  },
30
30
  "license": "MIT",
31
31
  "repository": {