fireflies-api 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +28 -0
  2. package/dist/cli/index.cjs +1435 -217
  3. package/dist/cli/index.cjs.map +1 -1
  4. package/dist/cli/index.js +1436 -222
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/index.cjs +1372 -392
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.cts +462 -3
  9. package/dist/index.d.ts +462 -3
  10. package/dist/index.js +1357 -393
  11. package/dist/index.js.map +1 -1
  12. package/dist/middleware/express.cjs +459 -16
  13. package/dist/middleware/express.cjs.map +1 -1
  14. package/dist/middleware/express.d.cts +2 -2
  15. package/dist/middleware/express.d.ts +2 -2
  16. package/dist/middleware/express.js +459 -16
  17. package/dist/middleware/express.js.map +1 -1
  18. package/dist/middleware/fastify.cjs +459 -16
  19. package/dist/middleware/fastify.cjs.map +1 -1
  20. package/dist/middleware/fastify.d.cts +2 -2
  21. package/dist/middleware/fastify.d.ts +2 -2
  22. package/dist/middleware/fastify.js +459 -16
  23. package/dist/middleware/fastify.js.map +1 -1
  24. package/dist/middleware/hono.cjs +459 -16
  25. package/dist/middleware/hono.cjs.map +1 -1
  26. package/dist/middleware/hono.d.cts +2 -2
  27. package/dist/middleware/hono.d.ts +2 -2
  28. package/dist/middleware/hono.js +459 -16
  29. package/dist/middleware/hono.js.map +1 -1
  30. package/dist/templates/digest/compact.md +8 -0
  31. package/dist/templates/digest/default.md +44 -0
  32. package/dist/templates/digest/executive.md +22 -0
  33. package/dist/{types-CaHcwnKw.d.ts → types-BMzVSd6w.d.ts} +1 -1
  34. package/dist/{types-BX-3JcRI.d.cts → types-BeXRmVD7.d.cts} +1 -1
  35. package/dist/{types-DIPZmUl3.d.ts → types-D2XsCR5R.d.ts} +120 -1
  36. package/dist/{types-C_XxdRd1.d.cts → types-zVGqyFzP.d.cts} +120 -1
  37. package/package.json +8 -2
@@ -1,6 +1,6 @@
1
1
  import { Handler } from 'hono';
2
- import { W as WebhookMiddlewareOptions } from '../types-BX-3JcRI.cjs';
3
- import '../types-C_XxdRd1.cjs';
2
+ import { W as WebhookMiddlewareOptions } from '../types-BeXRmVD7.cjs';
3
+ import '../types-zVGqyFzP.cjs';
4
4
  import '../action-items-CC9yUxHY.cjs';
5
5
 
6
6
  /**
@@ -1,6 +1,6 @@
1
1
  import { Handler } from 'hono';
2
- import { W as WebhookMiddlewareOptions } from '../types-CaHcwnKw.js';
3
- import '../types-DIPZmUl3.js';
2
+ import { W as WebhookMiddlewareOptions } from '../types-BMzVSd6w.js';
3
+ import '../types-D2XsCR5R.js';
4
4
  import '../action-items-CC9yUxHY.js';
5
5
 
6
6
  /**
@@ -289,8 +289,8 @@ async function retry(fn, options) {
289
289
  if (attempt >= maxRetries || !shouldRetry(error, attempt)) {
290
290
  throw error;
291
291
  }
292
- const delay = calculateDelay(error, attempt, baseDelay, maxDelay);
293
- await sleep(delay);
292
+ const delay2 = calculateDelay(error, attempt, baseDelay, maxDelay);
293
+ await sleep(delay2);
294
294
  }
295
295
  }
296
296
  throw lastError;
@@ -439,9 +439,9 @@ var GraphQLClient = class {
439
439
  if (!this.rateLimitTracker || !this.rateLimitConfig?.throttle) {
440
440
  return;
441
441
  }
442
- const delay = this.rateLimitTracker.getThrottleDelay(this.rateLimitConfig.throttle);
443
- if (delay > 0) {
444
- await sleep2(delay);
442
+ const delay2 = this.rateLimitTracker.getThrottleDelay(this.rateLimitConfig.throttle);
443
+ if (delay2 > 0) {
444
+ await sleep2(delay2);
445
445
  }
446
446
  }
447
447
  /**
@@ -1095,6 +1095,46 @@ function buildAggregatedResult(items, transcriptsProcessed, transcriptsWithItems
1095
1095
  };
1096
1096
  }
1097
1097
 
1098
+ // src/helpers/batch.ts
1099
+ async function* batch(items, processor, options = {}) {
1100
+ const { delayMs = 100, handleRateLimit = true, maxRateLimitRetries = 3 } = options;
1101
+ let isFirst = true;
1102
+ for await (const item of items) {
1103
+ if (!isFirst && delayMs > 0) {
1104
+ await delay(delayMs);
1105
+ }
1106
+ isFirst = false;
1107
+ yield await processWithRetry(item, processor, {
1108
+ handleRateLimit,
1109
+ maxRateLimitRetries
1110
+ });
1111
+ }
1112
+ }
1113
+ async function processWithRetry(item, processor, options) {
1114
+ const { handleRateLimit, maxRateLimitRetries } = options;
1115
+ let retries = 0;
1116
+ while (true) {
1117
+ try {
1118
+ const result = await processor(item);
1119
+ return { item, result };
1120
+ } catch (err) {
1121
+ if (handleRateLimit && err instanceof RateLimitError && retries < maxRateLimitRetries) {
1122
+ const waitTime = err.retryAfter ?? 1e3;
1123
+ await delay(waitTime);
1124
+ retries++;
1125
+ continue;
1126
+ }
1127
+ return {
1128
+ item,
1129
+ error: err instanceof Error ? err : new Error(String(err))
1130
+ };
1131
+ }
1132
+ }
1133
+ }
1134
+ function delay(ms) {
1135
+ return new Promise((resolve) => setTimeout(resolve, ms));
1136
+ }
1137
+
1098
1138
  // src/helpers/domain-utils.ts
1099
1139
  function extractDomain(email) {
1100
1140
  const atIndex = email.indexOf("@");
@@ -1110,6 +1150,332 @@ function hasExternalParticipants(participants, internalDomain) {
1110
1150
  });
1111
1151
  }
1112
1152
 
1153
+ // src/helpers/markdown.ts
1154
+ var DEFAULT_OPTIONS2 = {
1155
+ includeMetadata: true,
1156
+ includeSummary: true,
1157
+ includeActionItems: true,
1158
+ actionItemFormat: "checkbox",
1159
+ includeTimestamps: false,
1160
+ speakerFormat: "bold",
1161
+ groupBySpeaker: true
1162
+ };
1163
+ async function transcriptToMarkdown(transcript, options = {}) {
1164
+ const opts = { ...DEFAULT_OPTIONS2, ...options };
1165
+ const sections = [];
1166
+ if (opts.includeMetadata) {
1167
+ sections.push(formatMetadata(transcript));
1168
+ }
1169
+ if (opts.includeSummary && transcript.summary) {
1170
+ sections.push(formatSummary(transcript.summary, opts));
1171
+ }
1172
+ if (transcript.sentences && transcript.sentences.length > 0) {
1173
+ sections.push(formatTranscript(transcript.sentences, opts));
1174
+ }
1175
+ const content = sections.join("\n\n---\n\n");
1176
+ await writeIfOutputPath(content, options.outputPath);
1177
+ return content;
1178
+ }
1179
+ function formatMetadata(transcript) {
1180
+ const lines = [`# ${transcript.title || "Untitled Meeting"}`];
1181
+ if (transcript.dateString) {
1182
+ lines.push(`
1183
+ **Date:** ${formatDate(transcript.dateString)}`);
1184
+ }
1185
+ const duration = calculateDuration(transcript);
1186
+ if (duration > 0) {
1187
+ lines.push(`**Duration:** ${formatDuration(duration)}`);
1188
+ }
1189
+ const participants = getParticipantNames(transcript);
1190
+ if (participants.length > 0) {
1191
+ lines.push(`**Participants:** ${participants.join(", ")}`);
1192
+ }
1193
+ return lines.join("\n");
1194
+ }
1195
+ function calculateDuration(transcript) {
1196
+ if (transcript.sentences && transcript.sentences.length > 0) {
1197
+ const lastSentence = transcript.sentences[transcript.sentences.length - 1];
1198
+ if (lastSentence) {
1199
+ return parseFloat(lastSentence.end_time);
1200
+ }
1201
+ }
1202
+ return transcript.duration || 0;
1203
+ }
1204
+ function formatSummary(summary, opts) {
1205
+ const sections = ["## Summary"];
1206
+ if (summary.gist) {
1207
+ sections.push("", summary.gist);
1208
+ }
1209
+ if (summary.bullet_gist) {
1210
+ const bullets = parseMultilineField(summary.bullet_gist);
1211
+ if (bullets.length > 0) {
1212
+ sections.push("", "### Key Points");
1213
+ sections.push(bullets.map((p) => `- ${p}`).join("\n"));
1214
+ }
1215
+ }
1216
+ if (opts.includeActionItems && summary.action_items) {
1217
+ const items = parseMultilineField(summary.action_items);
1218
+ if (items.length > 0) {
1219
+ sections.push("", "### Action Items");
1220
+ const prefix = opts.actionItemFormat === "checkbox" ? "- [ ] " : "- ";
1221
+ sections.push(items.map((a) => `${prefix}${a}`).join("\n"));
1222
+ }
1223
+ }
1224
+ return sections.join("\n");
1225
+ }
1226
+ function formatTranscript(sentences, opts) {
1227
+ const lines = ["## Transcript"];
1228
+ if (opts.groupBySpeaker) {
1229
+ const groups = groupSentencesBySpeaker(sentences);
1230
+ for (const group of groups) {
1231
+ lines.push("", formatSpeakerGroup(group, opts));
1232
+ }
1233
+ } else {
1234
+ for (const sentence of sentences) {
1235
+ lines.push("", formatSentence(sentence, opts));
1236
+ }
1237
+ }
1238
+ return lines.join("\n");
1239
+ }
1240
+ function groupSentencesBySpeaker(sentences) {
1241
+ const groups = [];
1242
+ let current = null;
1243
+ for (const sentence of sentences) {
1244
+ if (!current || current.speakerName !== sentence.speaker_name) {
1245
+ current = { speakerName: sentence.speaker_name, sentences: [] };
1246
+ groups.push(current);
1247
+ }
1248
+ current.sentences.push(sentence);
1249
+ }
1250
+ return groups;
1251
+ }
1252
+ function formatSpeakerGroup(group, opts) {
1253
+ const speaker = formatSpeakerName(group.speakerName, opts.speakerFormat);
1254
+ const text = group.sentences.map((s) => s.text).join(" ");
1255
+ const firstSentence = group.sentences[0];
1256
+ if (opts.includeTimestamps && firstSentence) {
1257
+ const timestamp = formatTimestamp(firstSentence.start_time);
1258
+ return `${timestamp} ${speaker} ${text}`;
1259
+ }
1260
+ return `${speaker} ${text}`;
1261
+ }
1262
+ function formatSentence(sentence, opts) {
1263
+ const speaker = formatSpeakerName(sentence.speaker_name, opts.speakerFormat);
1264
+ if (opts.includeTimestamps) {
1265
+ const timestamp = formatTimestamp(sentence.start_time);
1266
+ return `${timestamp} ${speaker} ${sentence.text}`;
1267
+ }
1268
+ return `${speaker} ${sentence.text}`;
1269
+ }
1270
+ function formatSpeakerName(name, format) {
1271
+ switch (format) {
1272
+ case "bold":
1273
+ return `**${name}:**`;
1274
+ case "plain":
1275
+ return `${name}:`;
1276
+ }
1277
+ }
1278
+ function formatTimestamp(startTime) {
1279
+ const seconds = parseFloat(startTime);
1280
+ const mins = Math.floor(seconds / 60);
1281
+ const secs = Math.floor(seconds % 60);
1282
+ return `[${mins}:${secs.toString().padStart(2, "0")}]`;
1283
+ }
1284
+ function formatDuration(seconds) {
1285
+ const hours = Math.floor(seconds / 3600);
1286
+ const mins = Math.floor(seconds % 3600 / 60);
1287
+ if (hours > 0) {
1288
+ return `${hours}h ${mins}m`;
1289
+ }
1290
+ return `${mins} minutes`;
1291
+ }
1292
+ function formatDate(isoString) {
1293
+ return new Date(isoString).toLocaleDateString("en-US", {
1294
+ weekday: "long",
1295
+ year: "numeric",
1296
+ month: "long",
1297
+ day: "numeric"
1298
+ });
1299
+ }
1300
+ function getParticipantNames(transcript) {
1301
+ if (transcript.meeting_attendees?.length) {
1302
+ return transcript.meeting_attendees.map((a) => a.displayName || a.name || a.email).filter(Boolean);
1303
+ }
1304
+ return transcript.speakers?.map((s) => s.name) || [];
1305
+ }
1306
+ function parseMultilineField(value) {
1307
+ return value.split(/\n/).map((line) => line.trim()).filter((line) => line.length > 0);
1308
+ }
1309
+ async function writeIfOutputPath(content, outputPath) {
1310
+ if (outputPath) {
1311
+ const { writeFile } = await import('fs/promises');
1312
+ await writeFile(outputPath, content, "utf-8");
1313
+ }
1314
+ }
1315
+
1316
+ // src/helpers/export-formats.ts
1317
+ var DEFAULT_TEXT_OPTIONS = {
1318
+ includeTimestamps: false,
1319
+ includeMetadata: true
1320
+ };
1321
+ var DEFAULT_CSV_OPTIONS = {
1322
+ includeHeader: true,
1323
+ delimiter: ","
1324
+ };
1325
+ function transcriptToText(transcript, options = {}) {
1326
+ const opts = { ...DEFAULT_TEXT_OPTIONS, ...options };
1327
+ const sections = [];
1328
+ if (opts.includeMetadata) {
1329
+ sections.push(formatTextMetadata(transcript));
1330
+ }
1331
+ if (transcript.sentences && transcript.sentences.length > 0) {
1332
+ sections.push(formatTextTranscript(transcript.sentences, opts));
1333
+ }
1334
+ return sections.join("\n\n");
1335
+ }
1336
+ function transcriptToCsv(transcript, options = {}) {
1337
+ const opts = { ...DEFAULT_CSV_OPTIONS, ...options };
1338
+ const d = opts.delimiter;
1339
+ const lines = [];
1340
+ if (opts.includeHeader) {
1341
+ lines.push(`timestamp${d}speaker${d}text${d}is_question${d}is_task`);
1342
+ }
1343
+ for (const sentence of transcript.sentences) {
1344
+ const isQuestion = Boolean(sentence.ai_filters?.question);
1345
+ const isTask = Boolean(sentence.ai_filters?.task);
1346
+ const row = [
1347
+ sentence.start_time,
1348
+ escapeCsvField(sentence.speaker_name, d),
1349
+ escapeCsvField(sentence.text, d),
1350
+ String(isQuestion),
1351
+ String(isTask)
1352
+ ];
1353
+ lines.push(row.join(d));
1354
+ }
1355
+ return lines.join("\n");
1356
+ }
1357
+ function sanitizeFilename(title) {
1358
+ if (!title.trim()) {
1359
+ return "untitled";
1360
+ }
1361
+ return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 100);
1362
+ }
1363
+ function generateExportFilename(transcript, extension) {
1364
+ const sanitizedTitle = sanitizeFilename(transcript.title);
1365
+ let datePrefix = "";
1366
+ if (transcript.dateString) {
1367
+ try {
1368
+ const date = new Date(transcript.dateString);
1369
+ if (!Number.isNaN(date.getTime())) {
1370
+ datePrefix = `${date.toISOString().slice(0, 10)}-`;
1371
+ }
1372
+ } catch {
1373
+ }
1374
+ }
1375
+ return `${datePrefix}${sanitizedTitle}.${extension}`;
1376
+ }
1377
+ async function exportTranscript(transcript, format) {
1378
+ switch (format) {
1379
+ case "markdown":
1380
+ return transcriptToMarkdown(transcript);
1381
+ case "json":
1382
+ return JSON.stringify(transcript, null, 2);
1383
+ case "txt":
1384
+ return transcriptToText(transcript);
1385
+ case "csv":
1386
+ return transcriptToCsv(transcript);
1387
+ }
1388
+ }
1389
+ async function createZipArchive(files) {
1390
+ const archiver = await import('archiver');
1391
+ const { Writable } = await import('stream');
1392
+ return new Promise((resolve, reject) => {
1393
+ const chunks = [];
1394
+ const archive = archiver.default("zip", { zlib: { level: 9 } });
1395
+ const writable = new Writable({
1396
+ write(chunk, _encoding, callback) {
1397
+ chunks.push(chunk);
1398
+ callback();
1399
+ }
1400
+ });
1401
+ writable.on("finish", () => {
1402
+ resolve(Buffer.concat(chunks));
1403
+ });
1404
+ archive.on("error", reject);
1405
+ archive.pipe(writable);
1406
+ for (const file of files) {
1407
+ archive.append(file.content, { name: file.filename });
1408
+ }
1409
+ archive.finalize();
1410
+ });
1411
+ }
1412
+ function formatTextMetadata(transcript) {
1413
+ const lines = [];
1414
+ lines.push(transcript.title || "Untitled Meeting");
1415
+ if (transcript.dateString) {
1416
+ lines.push(`Date: ${formatDate2(transcript.dateString)}`);
1417
+ }
1418
+ const participants = getParticipantNames2(transcript);
1419
+ if (participants.length > 0) {
1420
+ lines.push(`Participants: ${participants.join(", ")}`);
1421
+ }
1422
+ return lines.join("\n");
1423
+ }
1424
+ function formatTextTranscript(sentences, opts) {
1425
+ const groups = groupSentencesBySpeaker2(sentences);
1426
+ const lines = [];
1427
+ for (const group of groups) {
1428
+ const text = group.sentences.map((s) => s.text).join(" ");
1429
+ const speaker = group.speakerName || "Unknown";
1430
+ const firstSentence = group.sentences[0];
1431
+ if (opts.includeTimestamps && firstSentence) {
1432
+ const timestamp = formatTimestamp2(firstSentence.start_time);
1433
+ lines.push(`${timestamp} ${speaker}: ${text}`);
1434
+ } else {
1435
+ lines.push(`${speaker}: ${text}`);
1436
+ }
1437
+ }
1438
+ return lines.join("\n");
1439
+ }
1440
+ function groupSentencesBySpeaker2(sentences) {
1441
+ const groups = [];
1442
+ let current = null;
1443
+ for (const sentence of sentences) {
1444
+ if (!current || current.speakerName !== sentence.speaker_name) {
1445
+ current = { speakerName: sentence.speaker_name, sentences: [] };
1446
+ groups.push(current);
1447
+ }
1448
+ current.sentences.push(sentence);
1449
+ }
1450
+ return groups;
1451
+ }
1452
+ function formatTimestamp2(startTime) {
1453
+ const seconds = parseFloat(startTime);
1454
+ const mins = Math.floor(seconds / 60);
1455
+ const secs = Math.floor(seconds % 60);
1456
+ return `[${mins}:${secs.toString().padStart(2, "0")}]`;
1457
+ }
1458
+ function formatDate2(isoString) {
1459
+ return new Date(isoString).toLocaleDateString("en-US", {
1460
+ weekday: "long",
1461
+ year: "numeric",
1462
+ month: "long",
1463
+ day: "numeric"
1464
+ });
1465
+ }
1466
+ function getParticipantNames2(transcript) {
1467
+ if (transcript.meeting_attendees?.length) {
1468
+ return transcript.meeting_attendees.map((a) => a.displayName || a.name || a.email).filter(Boolean);
1469
+ }
1470
+ return transcript.speakers?.map((s) => s.name) || [];
1471
+ }
1472
+ function escapeCsvField(field, delimiter) {
1473
+ if (field.includes('"') || field.includes(delimiter) || field.includes("\n")) {
1474
+ return `"${field.replace(/"/g, '""')}"`;
1475
+ }
1476
+ return field;
1477
+ }
1478
+
1113
1479
  // src/helpers/meeting-insights.ts
1114
1480
  function analyzeMeetings(transcripts, options = {}) {
1115
1481
  const { speakers, groupBy, topSpeakersCount = 10, topParticipantsCount = 10 } = options;
@@ -1583,6 +1949,21 @@ var TRANSCRIPT_LIST_FIELDS = `
1583
1949
  summary_status
1584
1950
  }
1585
1951
  `;
1952
+ function buildListFields(params) {
1953
+ const includeSentences = params?.includeSentences === true;
1954
+ const includeSummary = params?.includeSummary === true;
1955
+ if (!includeSentences && !includeSummary) {
1956
+ return TRANSCRIPT_LIST_FIELDS;
1957
+ }
1958
+ let fields = TRANSCRIPT_BASE_FIELDS;
1959
+ if (includeSentences) {
1960
+ fields += SENTENCES_FIELDS;
1961
+ }
1962
+ if (includeSummary) {
1963
+ fields += SUMMARY_FIELDS;
1964
+ }
1965
+ return fields;
1966
+ }
1586
1967
  function createTranscriptsAPI(client) {
1587
1968
  return {
1588
1969
  async get(id, params) {
@@ -1598,6 +1979,7 @@ function createTranscriptsAPI(client) {
1598
1979
  return normalizeTranscript(data.transcript);
1599
1980
  },
1600
1981
  async list(params) {
1982
+ const fields = buildListFields(params);
1601
1983
  const query = `
1602
1984
  query ListTranscripts(
1603
1985
  $keyword: String
@@ -1635,13 +2017,23 @@ function createTranscriptsAPI(client) {
1635
2017
  participant_email: $participant_email
1636
2018
  date: $date
1637
2019
  ) {
1638
- ${TRANSCRIPT_LIST_FIELDS}
2020
+ ${fields}
1639
2021
  }
1640
2022
  }
1641
2023
  `;
2024
+ let internalDomain;
2025
+ if (params?.external) {
2026
+ const userQuery = "query { user { email } }";
2027
+ const userData = await client.execute(userQuery);
2028
+ internalDomain = extractDomain(userData.user.email);
2029
+ }
1642
2030
  const variables = buildListVariables(params);
1643
2031
  const data = await client.execute(query, variables);
1644
- return data.transcripts.map(normalizeTranscript);
2032
+ let results = data.transcripts.map(normalizeTranscript);
2033
+ if (internalDomain) {
2034
+ results = results.filter((t) => hasExternalParticipants(t.participants, internalDomain));
2035
+ }
2036
+ return results;
1645
2037
  },
1646
2038
  async getSummary(id) {
1647
2039
  const query = `
@@ -1688,9 +2080,10 @@ function createTranscriptsAPI(client) {
1688
2080
  } = params;
1689
2081
  const transcripts = [];
1690
2082
  for await (const t of this.listAll({
2083
+ ...listParams,
1691
2084
  keyword: query,
1692
2085
  scope,
1693
- ...listParams
2086
+ includeSentences: true
1694
2087
  })) {
1695
2088
  transcripts.push(t);
1696
2089
  if (limit && transcripts.length >= limit) break;
@@ -1698,8 +2091,7 @@ function createTranscriptsAPI(client) {
1698
2091
  const allMatches = [];
1699
2092
  let transcriptsWithMatches = 0;
1700
2093
  for (const t of transcripts) {
1701
- const full = await this.get(t.id, { includeSentences: true });
1702
- const matches = searchTranscript(full, {
2094
+ const matches = searchTranscript(t, {
1703
2095
  query,
1704
2096
  caseSensitive,
1705
2097
  speakers,
@@ -1751,13 +2143,13 @@ function createTranscriptsAPI(client) {
1751
2143
  organizers,
1752
2144
  participants,
1753
2145
  user_id,
1754
- channel_id
2146
+ channel_id,
2147
+ includeSentences: true
1755
2148
  })) {
1756
2149
  if (internalDomain && !hasExternalParticipants(t.participants, internalDomain)) {
1757
2150
  continue;
1758
2151
  }
1759
- const full = await this.get(t.id, { includeSentences: true, includeSummary: false });
1760
- transcripts.push(full);
2152
+ transcripts.push(t);
1761
2153
  if (limit && transcripts.length >= limit) break;
1762
2154
  }
1763
2155
  return analyzeMeetings(transcripts, {
@@ -1775,13 +2167,20 @@ function createTranscriptsAPI(client) {
1775
2167
  toDate,
1776
2168
  mine,
1777
2169
  organizers,
1778
- participants
2170
+ participants,
2171
+ includeSummary: true
1779
2172
  })) {
1780
- const full = await this.get(t.id, { includeSentences: false, includeSummary: true });
1781
- transcripts.push(full);
2173
+ transcripts.push(t);
1782
2174
  if (limit && transcripts.length >= limit) break;
1783
2175
  }
1784
2176
  return aggregateActionItems(transcripts, {}, filterOptions);
2177
+ },
2178
+ async bulkExport(params = {}) {
2179
+ const { format = "markdown", asZip = false, onProgress } = params;
2180
+ const transcriptIds = await collectTranscriptIds(client, this, params);
2181
+ const files = await convertTranscripts(this, transcriptIds, format, onProgress);
2182
+ const zip = asZip ? await createZipArchive(files) : void 0;
2183
+ return { files, zip, format, totalExported: files.length };
1785
2184
  }
1786
2185
  };
1787
2186
  }
@@ -1860,6 +2259,50 @@ function buildListVariables(params) {
1860
2259
  date: params.date
1861
2260
  };
1862
2261
  }
2262
+ async function collectTranscriptIds(client, api, params) {
2263
+ const { ids, fromDate, toDate, mine, organizers, participants, external, limit } = params;
2264
+ if (ids?.length) {
2265
+ return ids;
2266
+ }
2267
+ let internalDomain;
2268
+ if (external) {
2269
+ const userQuery = "query { user { email } }";
2270
+ const userData = await client.execute(userQuery);
2271
+ internalDomain = extractDomain(userData.user.email);
2272
+ }
2273
+ const result = [];
2274
+ for await (const t of api.listAll({ fromDate, toDate, mine, organizers, participants })) {
2275
+ if (internalDomain && !hasExternalParticipants(t.participants, internalDomain)) {
2276
+ continue;
2277
+ }
2278
+ result.push(t.id);
2279
+ if (limit && result.length >= limit) break;
2280
+ }
2281
+ return result;
2282
+ }
2283
+ async function convertTranscripts(api, transcriptIds, format, onProgress) {
2284
+ const files = [];
2285
+ const extension = format === "markdown" ? "md" : format;
2286
+ let completed = 0;
2287
+ const total = transcriptIds.length;
2288
+ for await (const result of batch(
2289
+ transcriptIds,
2290
+ async (id) => {
2291
+ const transcript = await api.get(id);
2292
+ const content = await exportTranscript(transcript, format);
2293
+ const filename = generateExportFilename(transcript, extension);
2294
+ return { id, title: transcript.title, filename, content };
2295
+ },
2296
+ { delayMs: 100 }
2297
+ )) {
2298
+ if (!result.error) {
2299
+ files.push(result.result);
2300
+ }
2301
+ completed++;
2302
+ onProgress?.(completed, total);
2303
+ }
2304
+ return files;
2305
+ }
1863
2306
 
1864
2307
  // src/graphql/queries/users.ts
1865
2308
  var USER_FIELDS = `