@contractspec/integration.providers-impls 1.57.0 → 1.58.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 (256) hide show
  1. package/dist/analytics.d.ts +1 -8
  2. package/dist/analytics.d.ts.map +1 -1
  3. package/dist/analytics.js +3 -3
  4. package/dist/calendar.d.ts +1 -8
  5. package/dist/calendar.d.ts.map +1 -1
  6. package/dist/calendar.js +3 -3
  7. package/dist/database.d.ts +1 -8
  8. package/dist/database.d.ts.map +1 -1
  9. package/dist/database.js +3 -3
  10. package/dist/email.d.ts +1 -8
  11. package/dist/email.d.ts.map +1 -1
  12. package/dist/email.js +3 -3
  13. package/dist/embedding.d.ts +1 -8
  14. package/dist/embedding.d.ts.map +1 -1
  15. package/dist/embedding.js +3 -3
  16. package/dist/impls/elevenlabs-voice.d.ts +14 -18
  17. package/dist/impls/elevenlabs-voice.d.ts.map +1 -1
  18. package/dist/impls/elevenlabs-voice.js +98 -88
  19. package/dist/impls/fal-voice.d.ts +22 -26
  20. package/dist/impls/fal-voice.d.ts.map +1 -1
  21. package/dist/impls/fal-voice.js +103 -78
  22. package/dist/impls/fathom-meeting-recorder.d.ts +35 -39
  23. package/dist/impls/fathom-meeting-recorder.d.ts.map +1 -1
  24. package/dist/impls/fathom-meeting-recorder.js +285 -142
  25. package/dist/impls/fathom-meeting-recorder.mapper.d.ts +4 -8
  26. package/dist/impls/fathom-meeting-recorder.mapper.d.ts.map +1 -1
  27. package/dist/impls/fathom-meeting-recorder.mapper.js +102 -38
  28. package/dist/impls/fathom-meeting-recorder.types.d.ts +16 -20
  29. package/dist/impls/fathom-meeting-recorder.types.d.ts.map +1 -1
  30. package/dist/impls/fathom-meeting-recorder.types.js +1 -0
  31. package/dist/impls/fathom-meeting-recorder.utils.d.ts +10 -14
  32. package/dist/impls/fathom-meeting-recorder.utils.d.ts.map +1 -1
  33. package/dist/impls/fathom-meeting-recorder.utils.js +58 -41
  34. package/dist/impls/fathom-meeting-recorder.webhooks.d.ts +3 -7
  35. package/dist/impls/fathom-meeting-recorder.webhooks.d.ts.map +1 -1
  36. package/dist/impls/fathom-meeting-recorder.webhooks.js +25 -20
  37. package/dist/impls/fireflies-meeting-recorder.d.ts +21 -25
  38. package/dist/impls/fireflies-meeting-recorder.d.ts.map +1 -1
  39. package/dist/impls/fireflies-meeting-recorder.js +272 -149
  40. package/dist/impls/fireflies-meeting-recorder.queries.d.ts +3 -6
  41. package/dist/impls/fireflies-meeting-recorder.queries.d.ts.map +1 -1
  42. package/dist/impls/fireflies-meeting-recorder.queries.js +10 -8
  43. package/dist/impls/fireflies-meeting-recorder.types.d.ts +26 -29
  44. package/dist/impls/fireflies-meeting-recorder.types.d.ts.map +1 -1
  45. package/dist/impls/fireflies-meeting-recorder.types.js +1 -0
  46. package/dist/impls/fireflies-meeting-recorder.utils.d.ts +4 -7
  47. package/dist/impls/fireflies-meeting-recorder.utils.d.ts.map +1 -1
  48. package/dist/impls/fireflies-meeting-recorder.utils.js +34 -27
  49. package/dist/impls/gcs-storage.d.ts +18 -22
  50. package/dist/impls/gcs-storage.d.ts.map +1 -1
  51. package/dist/impls/gcs-storage.js +92 -84
  52. package/dist/impls/gmail-inbound.d.ts +20 -24
  53. package/dist/impls/gmail-inbound.d.ts.map +1 -1
  54. package/dist/impls/gmail-inbound.js +212 -185
  55. package/dist/impls/gmail-outbound.d.ts +12 -16
  56. package/dist/impls/gmail-outbound.d.ts.map +1 -1
  57. package/dist/impls/gmail-outbound.js +126 -92
  58. package/dist/impls/google-calendar.d.ts +17 -21
  59. package/dist/impls/google-calendar.d.ts.map +1 -1
  60. package/dist/impls/google-calendar.js +182 -145
  61. package/dist/impls/gradium-voice.d.ts +20 -22
  62. package/dist/impls/gradium-voice.d.ts.map +1 -1
  63. package/dist/impls/gradium-voice.js +85 -74
  64. package/dist/impls/granola-meeting-recorder.d.ts +31 -24
  65. package/dist/impls/granola-meeting-recorder.d.ts.map +1 -1
  66. package/dist/impls/granola-meeting-recorder.js +511 -143
  67. package/dist/impls/granola-meeting-recorder.mcp.d.ts +25 -0
  68. package/dist/impls/granola-meeting-recorder.mcp.d.ts.map +1 -0
  69. package/dist/impls/granola-meeting-recorder.mcp.js +279 -0
  70. package/dist/impls/granola-meeting-recorder.types.d.ts +60 -49
  71. package/dist/impls/granola-meeting-recorder.types.d.ts.map +1 -1
  72. package/dist/impls/granola-meeting-recorder.types.js +1 -0
  73. package/dist/impls/index.d.ts +28 -28
  74. package/dist/impls/index.d.ts.map +1 -0
  75. package/dist/impls/index.js +4659 -29
  76. package/dist/impls/jira.d.ts +18 -22
  77. package/dist/impls/jira.d.ts.map +1 -1
  78. package/dist/impls/jira.js +112 -101
  79. package/dist/impls/linear.d.ts +17 -21
  80. package/dist/impls/linear.d.ts.map +1 -1
  81. package/dist/impls/linear.js +78 -69
  82. package/dist/impls/mistral-embedding.d.ts +17 -21
  83. package/dist/impls/mistral-embedding.d.ts.map +1 -1
  84. package/dist/impls/mistral-embedding.js +41 -39
  85. package/dist/impls/mistral-llm.d.ts +25 -29
  86. package/dist/impls/mistral-llm.d.ts.map +1 -1
  87. package/dist/impls/mistral-llm.js +266 -244
  88. package/dist/impls/notion.d.ts +20 -24
  89. package/dist/impls/notion.d.ts.map +1 -1
  90. package/dist/impls/notion.js +145 -110
  91. package/dist/impls/posthog-reader.d.ts +18 -22
  92. package/dist/impls/posthog-reader.d.ts.map +1 -1
  93. package/dist/impls/posthog-reader.js +148 -129
  94. package/dist/impls/posthog-utils.d.ts +4 -7
  95. package/dist/impls/posthog-utils.d.ts.map +1 -1
  96. package/dist/impls/posthog-utils.js +31 -22
  97. package/dist/impls/posthog.d.ts +33 -37
  98. package/dist/impls/posthog.d.ts.map +1 -1
  99. package/dist/impls/posthog.js +320 -119
  100. package/dist/impls/postmark-email.d.ts +13 -17
  101. package/dist/impls/postmark-email.d.ts.map +1 -1
  102. package/dist/impls/postmark-email.js +55 -50
  103. package/dist/impls/powens-client.d.ts +111 -114
  104. package/dist/impls/powens-client.d.ts.map +1 -1
  105. package/dist/impls/powens-client.js +194 -170
  106. package/dist/impls/powens-openbanking.d.ts +22 -26
  107. package/dist/impls/powens-openbanking.d.ts.map +1 -1
  108. package/dist/impls/powens-openbanking.js +425 -217
  109. package/dist/impls/provider-factory.d.ts +29 -33
  110. package/dist/impls/provider-factory.d.ts.map +1 -1
  111. package/dist/impls/provider-factory.js +4072 -275
  112. package/dist/impls/qdrant-vector.d.ts +18 -22
  113. package/dist/impls/qdrant-vector.d.ts.map +1 -1
  114. package/dist/impls/qdrant-vector.js +76 -69
  115. package/dist/impls/stripe-payments.d.ts +22 -26
  116. package/dist/impls/stripe-payments.d.ts.map +1 -1
  117. package/dist/impls/stripe-payments.js +219 -193
  118. package/dist/impls/supabase-psql.d.ts +21 -25
  119. package/dist/impls/supabase-psql.d.ts.map +1 -1
  120. package/dist/impls/supabase-psql.js +138 -98
  121. package/dist/impls/supabase-vector.d.ts +29 -33
  122. package/dist/impls/supabase-vector.d.ts.map +1 -1
  123. package/dist/impls/supabase-vector.js +278 -103
  124. package/dist/impls/tldv-meeting-recorder.d.ts +18 -22
  125. package/dist/impls/tldv-meeting-recorder.d.ts.map +1 -1
  126. package/dist/impls/tldv-meeting-recorder.js +142 -127
  127. package/dist/impls/twilio-sms.d.ts +14 -17
  128. package/dist/impls/twilio-sms.d.ts.map +1 -1
  129. package/dist/impls/twilio-sms.js +62 -55
  130. package/dist/index.d.ts +15 -64
  131. package/dist/index.d.ts.map +1 -1
  132. package/dist/index.js +4700 -107
  133. package/dist/llm.d.ts +1 -8
  134. package/dist/llm.d.ts.map +1 -1
  135. package/dist/llm.js +3 -3
  136. package/dist/meeting-recorder.d.ts +1 -8
  137. package/dist/meeting-recorder.d.ts.map +1 -1
  138. package/dist/meeting-recorder.js +3 -3
  139. package/dist/node/analytics.js +2 -0
  140. package/dist/node/calendar.js +2 -0
  141. package/dist/node/database.js +2 -0
  142. package/dist/node/email.js +2 -0
  143. package/dist/node/embedding.js +2 -0
  144. package/dist/node/impls/elevenlabs-voice.js +102 -0
  145. package/dist/node/impls/fal-voice.js +112 -0
  146. package/dist/node/impls/fathom-meeting-recorder.js +287 -0
  147. package/dist/node/impls/fathom-meeting-recorder.mapper.js +105 -0
  148. package/dist/node/impls/fathom-meeting-recorder.types.js +0 -0
  149. package/dist/node/impls/fathom-meeting-recorder.utils.js +72 -0
  150. package/dist/node/impls/fathom-meeting-recorder.webhooks.js +29 -0
  151. package/dist/node/impls/fireflies-meeting-recorder.js +274 -0
  152. package/dist/node/impls/fireflies-meeting-recorder.queries.js +85 -0
  153. package/dist/node/impls/fireflies-meeting-recorder.types.js +0 -0
  154. package/dist/node/impls/fireflies-meeting-recorder.utils.js +42 -0
  155. package/dist/node/impls/gcs-storage.js +97 -0
  156. package/dist/node/impls/gmail-inbound.js +227 -0
  157. package/dist/node/impls/gmail-outbound.js +139 -0
  158. package/dist/node/impls/google-calendar.js +191 -0
  159. package/dist/node/impls/gradium-voice.js +90 -0
  160. package/dist/node/impls/granola-meeting-recorder.js +512 -0
  161. package/dist/node/impls/granola-meeting-recorder.mcp.js +278 -0
  162. package/dist/node/impls/granola-meeting-recorder.types.js +0 -0
  163. package/dist/node/impls/index.js +4658 -0
  164. package/dist/node/impls/jira.js +124 -0
  165. package/dist/node/impls/linear.js +83 -0
  166. package/dist/node/impls/mistral-embedding.js +43 -0
  167. package/dist/node/impls/mistral-llm.js +269 -0
  168. package/dist/node/impls/notion.js +160 -0
  169. package/dist/node/impls/posthog-reader.js +159 -0
  170. package/dist/node/impls/posthog-utils.js +38 -0
  171. package/dist/node/impls/posthog.js +322 -0
  172. package/dist/node/impls/postmark-email.js +60 -0
  173. package/dist/node/impls/powens-client.js +195 -0
  174. package/dist/node/impls/powens-openbanking.js +426 -0
  175. package/dist/node/impls/provider-factory.js +4080 -0
  176. package/dist/node/impls/qdrant-vector.js +78 -0
  177. package/dist/node/impls/stripe-payments.js +228 -0
  178. package/dist/node/impls/supabase-psql.js +150 -0
  179. package/dist/node/impls/supabase-vector.js +323 -0
  180. package/dist/node/impls/tldv-meeting-recorder.js +145 -0
  181. package/dist/node/impls/twilio-sms.js +65 -0
  182. package/dist/node/index.js +4699 -0
  183. package/dist/node/llm.js +2 -0
  184. package/dist/node/meeting-recorder.js +2 -0
  185. package/dist/node/openbanking.js +2 -0
  186. package/dist/node/payments.js +2 -0
  187. package/dist/node/project-management.js +2 -0
  188. package/dist/node/runtime.js +0 -0
  189. package/dist/node/secrets/provider.js +11 -0
  190. package/dist/node/sms.js +2 -0
  191. package/dist/node/storage.js +2 -0
  192. package/dist/node/vector-store.js +2 -0
  193. package/dist/node/voice.js +2 -0
  194. package/dist/openbanking.d.ts +1 -8
  195. package/dist/openbanking.d.ts.map +1 -1
  196. package/dist/openbanking.js +3 -3
  197. package/dist/payments.d.ts +1 -8
  198. package/dist/payments.d.ts.map +1 -1
  199. package/dist/payments.js +3 -3
  200. package/dist/project-management.d.ts +1 -8
  201. package/dist/project-management.d.ts.map +1 -1
  202. package/dist/project-management.js +3 -3
  203. package/dist/runtime.d.ts +2 -2
  204. package/dist/runtime.d.ts.map +1 -0
  205. package/dist/runtime.js +1 -0
  206. package/dist/secrets/provider.d.ts +3 -2
  207. package/dist/secrets/provider.d.ts.map +1 -0
  208. package/dist/secrets/provider.js +12 -3
  209. package/dist/sms.d.ts +1 -8
  210. package/dist/sms.d.ts.map +1 -1
  211. package/dist/sms.js +3 -3
  212. package/dist/storage.d.ts +1 -8
  213. package/dist/storage.d.ts.map +1 -1
  214. package/dist/storage.js +3 -3
  215. package/dist/vector-store.d.ts +1 -8
  216. package/dist/vector-store.d.ts.map +1 -1
  217. package/dist/vector-store.js +3 -3
  218. package/dist/voice.d.ts +1 -8
  219. package/dist/voice.d.ts.map +1 -1
  220. package/dist/voice.js +3 -3
  221. package/package.json +405 -114
  222. package/dist/_virtual/_rolldown/runtime.js +0 -36
  223. package/dist/impls/elevenlabs-voice.js.map +0 -1
  224. package/dist/impls/fal-voice.js.map +0 -1
  225. package/dist/impls/fathom-meeting-recorder.js.map +0 -1
  226. package/dist/impls/fathom-meeting-recorder.mapper.js.map +0 -1
  227. package/dist/impls/fathom-meeting-recorder.utils.js.map +0 -1
  228. package/dist/impls/fathom-meeting-recorder.webhooks.js.map +0 -1
  229. package/dist/impls/fireflies-meeting-recorder.js.map +0 -1
  230. package/dist/impls/fireflies-meeting-recorder.queries.js.map +0 -1
  231. package/dist/impls/fireflies-meeting-recorder.utils.js.map +0 -1
  232. package/dist/impls/gcs-storage.js.map +0 -1
  233. package/dist/impls/gmail-inbound.js.map +0 -1
  234. package/dist/impls/gmail-outbound.js.map +0 -1
  235. package/dist/impls/google-calendar.js.map +0 -1
  236. package/dist/impls/gradium-voice.js.map +0 -1
  237. package/dist/impls/granola-meeting-recorder.js.map +0 -1
  238. package/dist/impls/jira.js.map +0 -1
  239. package/dist/impls/linear.js.map +0 -1
  240. package/dist/impls/mistral-embedding.js.map +0 -1
  241. package/dist/impls/mistral-llm.js.map +0 -1
  242. package/dist/impls/notion.js.map +0 -1
  243. package/dist/impls/posthog-reader.js.map +0 -1
  244. package/dist/impls/posthog-utils.js.map +0 -1
  245. package/dist/impls/posthog.js.map +0 -1
  246. package/dist/impls/postmark-email.js.map +0 -1
  247. package/dist/impls/powens-client.js.map +0 -1
  248. package/dist/impls/powens-openbanking.js.map +0 -1
  249. package/dist/impls/provider-factory.js.map +0 -1
  250. package/dist/impls/qdrant-vector.js.map +0 -1
  251. package/dist/impls/stripe-payments.js.map +0 -1
  252. package/dist/impls/supabase-psql.js.map +0 -1
  253. package/dist/impls/supabase-vector.js.map +0 -1
  254. package/dist/impls/tldv-meeting-recorder.js.map +0 -1
  255. package/dist/impls/twilio-sms.js.map +0 -1
  256. package/dist/index.js.map +0 -1
@@ -0,0 +1,72 @@
1
+ // src/impls/fathom-meeting-recorder.utils.ts
2
+ function extractItems(page) {
3
+ if (Array.isArray(page.items))
4
+ return page.items;
5
+ if (Array.isArray(page.data)) {
6
+ return page.data;
7
+ }
8
+ return [];
9
+ }
10
+ function extractNextCursor(page) {
11
+ return page.nextCursor ?? page.next_cursor ?? undefined;
12
+ }
13
+ function mapInvitee(invitee) {
14
+ const email = invitee.email;
15
+ const name = invitee.name;
16
+ if (!email && !name)
17
+ return;
18
+ return {
19
+ email,
20
+ name,
21
+ role: "attendee",
22
+ isExternal: invitee.is_external
23
+ };
24
+ }
25
+ function matchRecordingId(meeting, targetId) {
26
+ return meeting.recordingId === targetId;
27
+ }
28
+ function durationSeconds(start, end) {
29
+ if (!start || !end)
30
+ return;
31
+ const startDate = start instanceof Date ? start : new Date(start);
32
+ const endDate = end instanceof Date ? end : new Date(end);
33
+ if (Number.isNaN(startDate.valueOf()) || Number.isNaN(endDate.valueOf())) {
34
+ return;
35
+ }
36
+ return Math.max(0, (endDate.valueOf() - startDate.valueOf()) / 1000);
37
+ }
38
+ function mapTranscriptSegment(segment, index) {
39
+ return {
40
+ index,
41
+ speakerName: segment.speaker?.display_name ?? undefined,
42
+ speakerEmail: segment.speaker?.matched_calendar_invitee_email ?? undefined,
43
+ text: segment.text,
44
+ startTimeMs: parseTimestamp(segment.timestamp)
45
+ };
46
+ }
47
+ function parseTimestamp(value) {
48
+ const parts = value.split(":").map((part) => Number(part));
49
+ if (parts.length !== 3 || parts.some((part) => Number.isNaN(part))) {
50
+ return;
51
+ }
52
+ const [hours = 0, minutes = 0, seconds = 0] = parts;
53
+ return (hours * 3600 + minutes * 60 + seconds) * 1000;
54
+ }
55
+ async function safeReadError(response) {
56
+ try {
57
+ const data = await response.json();
58
+ return data?.message ?? response.statusText;
59
+ } catch {
60
+ return response.statusText;
61
+ }
62
+ }
63
+ export {
64
+ safeReadError,
65
+ parseTimestamp,
66
+ matchRecordingId,
67
+ mapTranscriptSegment,
68
+ mapInvitee,
69
+ extractNextCursor,
70
+ extractItems,
71
+ durationSeconds
72
+ };
@@ -0,0 +1,29 @@
1
+ // src/impls/fathom-meeting-recorder.webhooks.ts
2
+ import { TriggeredFor } from "fathom-typescript/sdk/models/operations";
3
+ function normalizeWebhookHeaders(headers) {
4
+ const normalized = {};
5
+ for (const [key, value] of Object.entries(headers)) {
6
+ if (value == null)
7
+ continue;
8
+ const normalizedKey = key.toLowerCase();
9
+ if (Array.isArray(value)) {
10
+ if (value.length === 0)
11
+ continue;
12
+ normalized[normalizedKey] = value.join(", ");
13
+ } else {
14
+ normalized[normalizedKey] = value;
15
+ }
16
+ }
17
+ return normalized;
18
+ }
19
+ function normalizeTriggeredFor(values) {
20
+ if (!values)
21
+ return;
22
+ const allowed = new Set(Object.values(TriggeredFor));
23
+ const normalized = values.map((value) => value.trim()).filter((value) => allowed.has(value));
24
+ return normalized.length ? normalized : undefined;
25
+ }
26
+ export {
27
+ normalizeWebhookHeaders,
28
+ normalizeTriggeredFor
29
+ };
@@ -0,0 +1,274 @@
1
+ // src/impls/fireflies-meeting-recorder.queries.ts
2
+ var TRANSCRIPTS_QUERY = `
3
+ query Transcripts(
4
+ $limit: Int
5
+ $skip: Int
6
+ $fromDate: DateTime
7
+ $toDate: DateTime
8
+ $keyword: String
9
+ $scope: TranscriptsQueryScope
10
+ ) {
11
+ transcripts(
12
+ limit: $limit
13
+ skip: $skip
14
+ fromDate: $fromDate
15
+ toDate: $toDate
16
+ keyword: $keyword
17
+ scope: $scope
18
+ ) {
19
+ id
20
+ title
21
+ organizer_email
22
+ participants
23
+ meeting_attendees {
24
+ name
25
+ email
26
+ displayName
27
+ }
28
+ dateString
29
+ duration
30
+ meeting_link
31
+ transcript_url
32
+ }
33
+ }
34
+ `;
35
+ var TRANSCRIPT_QUERY = `
36
+ query Transcript($transcriptId: String!) {
37
+ transcript(id: $transcriptId) {
38
+ id
39
+ title
40
+ organizer_email
41
+ participants
42
+ meeting_attendees {
43
+ name
44
+ email
45
+ displayName
46
+ }
47
+ dateString
48
+ duration
49
+ meeting_link
50
+ transcript_url
51
+ }
52
+ }
53
+ `;
54
+ var TRANSCRIPT_WITH_SEGMENTS_QUERY = `
55
+ query Transcript($transcriptId: String!) {
56
+ transcript(id: $transcriptId) {
57
+ id
58
+ title
59
+ organizer_email
60
+ participants
61
+ meeting_attendees {
62
+ name
63
+ email
64
+ displayName
65
+ }
66
+ dateString
67
+ duration
68
+ meeting_link
69
+ transcript_url
70
+ sentences {
71
+ index
72
+ speaker_name
73
+ speaker_id
74
+ text
75
+ start_time
76
+ end_time
77
+ }
78
+ }
79
+ }
80
+ `;
81
+
82
+ // src/impls/fireflies-meeting-recorder.utils.ts
83
+ import { Buffer } from "node:buffer";
84
+ import { timingSafeEqual } from "crypto";
85
+ function parseSeconds(value) {
86
+ if (value == null)
87
+ return;
88
+ const num = typeof value === "number" ? value : Number(value);
89
+ if (!Number.isFinite(num))
90
+ return;
91
+ return num * 1000;
92
+ }
93
+ function normalizeHeader(headers, key) {
94
+ const header = headers[key] ?? headers[key.toLowerCase()] ?? headers[key.toUpperCase()];
95
+ if (Array.isArray(header))
96
+ return header[0];
97
+ return header;
98
+ }
99
+ function safeCompareHex(a, b) {
100
+ try {
101
+ const aBuffer = Buffer.from(a, "hex");
102
+ const bBuffer = Buffer.from(b, "hex");
103
+ if (aBuffer.length !== bBuffer.length)
104
+ return false;
105
+ return timingSafeEqual(aBuffer, bBuffer);
106
+ } catch {
107
+ return false;
108
+ }
109
+ }
110
+ async function safeReadError(response) {
111
+ try {
112
+ const data = await response.json();
113
+ return data?.message ?? response.statusText;
114
+ } catch {
115
+ return response.statusText;
116
+ }
117
+ }
118
+
119
+ // src/impls/fireflies-meeting-recorder.ts
120
+ import { createHmac } from "crypto";
121
+ var DEFAULT_BASE_URL = "https://api.fireflies.ai/graphql";
122
+
123
+ class FirefliesMeetingRecorderProvider {
124
+ apiKey;
125
+ baseUrl;
126
+ defaultPageSize;
127
+ webhookSecret;
128
+ constructor(options) {
129
+ this.apiKey = options.apiKey;
130
+ this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
131
+ this.defaultPageSize = options.pageSize;
132
+ this.webhookSecret = options.webhookSecret;
133
+ }
134
+ async listMeetings(params) {
135
+ const limit = params.pageSize ?? this.defaultPageSize ?? 25;
136
+ const skip = params.cursor ? Number(params.cursor) : 0;
137
+ const data = await this.query(TRANSCRIPTS_QUERY, {
138
+ limit,
139
+ skip: Number.isFinite(skip) ? skip : 0,
140
+ fromDate: params.from,
141
+ toDate: params.to,
142
+ keyword: params.query,
143
+ scope: params.query ? "all" : undefined
144
+ });
145
+ const meetings = data.transcripts.map((transcript) => this.mapTranscriptToMeeting(transcript, params));
146
+ const nextCursor = meetings.length === limit ? String(skip + limit) : undefined;
147
+ return {
148
+ meetings,
149
+ nextCursor,
150
+ hasMore: Boolean(nextCursor)
151
+ };
152
+ }
153
+ async getMeeting(params) {
154
+ const data = await this.query(TRANSCRIPT_QUERY, {
155
+ transcriptId: params.meetingId
156
+ });
157
+ return this.mapTranscriptToMeeting(data.transcript, params);
158
+ }
159
+ async getTranscript(params) {
160
+ const data = await this.query(TRANSCRIPT_WITH_SEGMENTS_QUERY, { transcriptId: params.meetingId });
161
+ const transcript = data.transcript;
162
+ const segments = (transcript.sentences ?? []).map((segment) => this.mapSentence(segment));
163
+ return {
164
+ id: transcript.id,
165
+ meetingId: transcript.id,
166
+ tenantId: params.tenantId,
167
+ connectionId: params.connectionId ?? "unknown",
168
+ externalId: transcript.id,
169
+ format: "segments",
170
+ text: segments.map((segment) => segment.text).join(`
171
+ `),
172
+ segments,
173
+ generatedAt: transcript.dateString ?? undefined,
174
+ sourceUrl: transcript.transcript_url ?? undefined,
175
+ metadata: {
176
+ meetingLink: transcript.meeting_link,
177
+ durationMinutes: transcript.duration
178
+ },
179
+ raw: transcript
180
+ };
181
+ }
182
+ async parseWebhook(request) {
183
+ const payload = request.parsedBody ?? JSON.parse(request.rawBody);
184
+ const body = payload;
185
+ const verified = this.webhookSecret ? await this.verifyWebhook(request) : undefined;
186
+ return {
187
+ providerKey: "meeting-recorder.fireflies",
188
+ eventType: body.eventType,
189
+ meetingId: body.meetingId,
190
+ transcriptId: body.meetingId,
191
+ verified,
192
+ payload,
193
+ metadata: {
194
+ clientReferenceId: body.clientReferenceId
195
+ }
196
+ };
197
+ }
198
+ async verifyWebhook(request) {
199
+ if (!this.webhookSecret)
200
+ return true;
201
+ const signatureHeader = normalizeHeader(request.headers, "x-hub-signature");
202
+ if (!signatureHeader)
203
+ return false;
204
+ const signature = signatureHeader.replace(/^sha256=/, "");
205
+ const digest = createHmac("sha256", this.webhookSecret).update(request.rawBody).digest("hex");
206
+ return safeCompareHex(digest, signature);
207
+ }
208
+ mapTranscriptToMeeting(transcript, params) {
209
+ const connectionId = params.connectionId ?? "unknown";
210
+ const organizer = transcript.organizer_email ? { email: transcript.organizer_email, role: "organizer" } : undefined;
211
+ const attendees = transcript.meeting_attendees?.length ? transcript.meeting_attendees.map((attendee) => this.mapAttendee(attendee)) : transcript.participants?.map((email) => ({ email, role: "attendee" }));
212
+ return {
213
+ id: transcript.id,
214
+ tenantId: params.tenantId,
215
+ connectionId,
216
+ externalId: transcript.id,
217
+ title: transcript.title ?? undefined,
218
+ organizer,
219
+ invitees: attendees,
220
+ participants: attendees,
221
+ scheduledStartAt: transcript.dateString ?? undefined,
222
+ recordingStartAt: transcript.dateString ?? undefined,
223
+ durationSeconds: transcript.duration ? transcript.duration * 60 : undefined,
224
+ meetingUrl: transcript.meeting_link ?? transcript.transcript_url ?? undefined,
225
+ transcriptAvailable: Boolean(transcript.transcript_url),
226
+ sourcePlatform: "fireflies",
227
+ metadata: {
228
+ transcriptUrl: transcript.transcript_url
229
+ }
230
+ };
231
+ }
232
+ mapAttendee(attendee) {
233
+ return {
234
+ name: attendee.name ?? attendee.displayName ?? undefined,
235
+ email: attendee.email ?? undefined,
236
+ role: "attendee"
237
+ };
238
+ }
239
+ mapSentence(segment) {
240
+ return {
241
+ index: segment.index ?? undefined,
242
+ speakerId: segment.speaker_id ?? undefined,
243
+ speakerName: segment.speaker_name ?? undefined,
244
+ text: segment.text,
245
+ startTimeMs: parseSeconds(segment.start_time),
246
+ endTimeMs: parseSeconds(segment.end_time)
247
+ };
248
+ }
249
+ async query(query, variables) {
250
+ const response = await fetch(this.baseUrl, {
251
+ method: "POST",
252
+ headers: {
253
+ "Content-Type": "application/json",
254
+ Authorization: `Bearer ${this.apiKey}`
255
+ },
256
+ body: JSON.stringify({ query, variables })
257
+ });
258
+ if (!response.ok) {
259
+ const message = await safeReadError(response);
260
+ throw new Error(`Fireflies API error (${response.status}): ${message}`);
261
+ }
262
+ const result = await response.json();
263
+ if (result.errors?.length) {
264
+ throw new Error(result.errors.map((error) => error.message).join("; "));
265
+ }
266
+ if (!result.data) {
267
+ throw new Error("Fireflies API returned empty data payload.");
268
+ }
269
+ return result.data;
270
+ }
271
+ }
272
+ export {
273
+ FirefliesMeetingRecorderProvider
274
+ };
@@ -0,0 +1,85 @@
1
+ // src/impls/fireflies-meeting-recorder.queries.ts
2
+ var TRANSCRIPTS_QUERY = `
3
+ query Transcripts(
4
+ $limit: Int
5
+ $skip: Int
6
+ $fromDate: DateTime
7
+ $toDate: DateTime
8
+ $keyword: String
9
+ $scope: TranscriptsQueryScope
10
+ ) {
11
+ transcripts(
12
+ limit: $limit
13
+ skip: $skip
14
+ fromDate: $fromDate
15
+ toDate: $toDate
16
+ keyword: $keyword
17
+ scope: $scope
18
+ ) {
19
+ id
20
+ title
21
+ organizer_email
22
+ participants
23
+ meeting_attendees {
24
+ name
25
+ email
26
+ displayName
27
+ }
28
+ dateString
29
+ duration
30
+ meeting_link
31
+ transcript_url
32
+ }
33
+ }
34
+ `;
35
+ var TRANSCRIPT_QUERY = `
36
+ query Transcript($transcriptId: String!) {
37
+ transcript(id: $transcriptId) {
38
+ id
39
+ title
40
+ organizer_email
41
+ participants
42
+ meeting_attendees {
43
+ name
44
+ email
45
+ displayName
46
+ }
47
+ dateString
48
+ duration
49
+ meeting_link
50
+ transcript_url
51
+ }
52
+ }
53
+ `;
54
+ var TRANSCRIPT_WITH_SEGMENTS_QUERY = `
55
+ query Transcript($transcriptId: String!) {
56
+ transcript(id: $transcriptId) {
57
+ id
58
+ title
59
+ organizer_email
60
+ participants
61
+ meeting_attendees {
62
+ name
63
+ email
64
+ displayName
65
+ }
66
+ dateString
67
+ duration
68
+ meeting_link
69
+ transcript_url
70
+ sentences {
71
+ index
72
+ speaker_name
73
+ speaker_id
74
+ text
75
+ start_time
76
+ end_time
77
+ }
78
+ }
79
+ }
80
+ `;
81
+ export {
82
+ TRANSCRIPT_WITH_SEGMENTS_QUERY,
83
+ TRANSCRIPT_QUERY,
84
+ TRANSCRIPTS_QUERY
85
+ };
@@ -0,0 +1,42 @@
1
+ // src/impls/fireflies-meeting-recorder.utils.ts
2
+ import { Buffer } from "node:buffer";
3
+ import { timingSafeEqual } from "crypto";
4
+ function parseSeconds(value) {
5
+ if (value == null)
6
+ return;
7
+ const num = typeof value === "number" ? value : Number(value);
8
+ if (!Number.isFinite(num))
9
+ return;
10
+ return num * 1000;
11
+ }
12
+ function normalizeHeader(headers, key) {
13
+ const header = headers[key] ?? headers[key.toLowerCase()] ?? headers[key.toUpperCase()];
14
+ if (Array.isArray(header))
15
+ return header[0];
16
+ return header;
17
+ }
18
+ function safeCompareHex(a, b) {
19
+ try {
20
+ const aBuffer = Buffer.from(a, "hex");
21
+ const bBuffer = Buffer.from(b, "hex");
22
+ if (aBuffer.length !== bBuffer.length)
23
+ return false;
24
+ return timingSafeEqual(aBuffer, bBuffer);
25
+ } catch {
26
+ return false;
27
+ }
28
+ }
29
+ async function safeReadError(response) {
30
+ try {
31
+ const data = await response.json();
32
+ return data?.message ?? response.statusText;
33
+ } catch {
34
+ return response.statusText;
35
+ }
36
+ }
37
+ export {
38
+ safeReadError,
39
+ safeCompareHex,
40
+ parseSeconds,
41
+ normalizeHeader
42
+ };
@@ -0,0 +1,97 @@
1
+ // src/impls/gcs-storage.ts
2
+ import { Storage } from "@google-cloud/storage";
3
+
4
+ class GoogleCloudStorageProvider {
5
+ storage;
6
+ bucketName;
7
+ constructor(options) {
8
+ this.storage = options.storage ?? new Storage(options.clientOptions ?? undefined);
9
+ this.bucketName = options.bucket;
10
+ }
11
+ async putObject(input) {
12
+ const bucketName = input.bucket ?? this.bucketName;
13
+ const bucket = this.storage.bucket(bucketName);
14
+ const file = bucket.file(input.key);
15
+ const buffer = toBuffer(input.data);
16
+ await file.save(buffer, {
17
+ resumable: false,
18
+ contentType: input.contentType,
19
+ metadata: input.metadata
20
+ });
21
+ if (input.makePublic) {
22
+ await file.makePublic();
23
+ }
24
+ const [metadata] = await file.getMetadata();
25
+ return toMetadata(metadata);
26
+ }
27
+ async getObject(input) {
28
+ const bucketName = input.bucket ?? this.bucketName;
29
+ const bucket = this.storage.bucket(bucketName);
30
+ const file = bucket.file(input.key);
31
+ const [exists] = await file.exists();
32
+ if (!exists)
33
+ return null;
34
+ const [contents] = await file.download();
35
+ const [metadata] = await file.getMetadata();
36
+ return {
37
+ ...toMetadata(metadata),
38
+ data: new Uint8Array(contents)
39
+ };
40
+ }
41
+ async deleteObject(input) {
42
+ const bucketName = input.bucket ?? this.bucketName;
43
+ const bucket = this.storage.bucket(bucketName);
44
+ const file = bucket.file(input.key);
45
+ await file.delete({ ignoreNotFound: true });
46
+ }
47
+ async generateSignedUrl(options) {
48
+ const bucketName = options.bucket ?? this.bucketName;
49
+ const bucket = this.storage.bucket(bucketName);
50
+ const file = bucket.file(options.key);
51
+ const action = options.method === "PUT" ? "write" : "read";
52
+ const expires = Date.now() + options.expiresInSeconds * 1000;
53
+ const [url] = await file.getSignedUrl({
54
+ action,
55
+ expires,
56
+ contentType: options.contentType
57
+ });
58
+ return { url, expiresAt: new Date(expires) };
59
+ }
60
+ async listObjects(query) {
61
+ const bucketName = query.bucket ?? this.bucketName;
62
+ const bucket = this.storage.bucket(bucketName);
63
+ const [files, nextQuery, response] = await bucket.getFiles({
64
+ prefix: query.prefix,
65
+ maxResults: query.maxResults,
66
+ pageToken: query.pageToken
67
+ });
68
+ const nextTokenFromQuery = typeof nextQuery === "object" && nextQuery !== null && "pageToken" in nextQuery ? nextQuery.pageToken : undefined;
69
+ const nextTokenFromResponse = response && typeof response === "object" && "nextPageToken" in response ? response.nextPageToken : undefined;
70
+ return {
71
+ objects: files.map((file) => toMetadata(file.metadata)),
72
+ nextPageToken: nextTokenFromQuery ?? nextTokenFromResponse ?? undefined
73
+ };
74
+ }
75
+ }
76
+ function toBuffer(data) {
77
+ if (data instanceof Uint8Array) {
78
+ return Buffer.from(data);
79
+ }
80
+ return Buffer.from(data);
81
+ }
82
+ function toMetadata(metadata) {
83
+ const meta = metadata;
84
+ return {
85
+ bucket: String(meta.bucket ?? ""),
86
+ key: String(meta.name ?? ""),
87
+ sizeBytes: meta.size ? Number(meta.size) : undefined,
88
+ contentType: meta.contentType ? String(meta.contentType) : undefined,
89
+ etag: meta.etag ? String(meta.etag) : undefined,
90
+ checksum: meta.md5Hash ? String(meta.md5Hash) : undefined,
91
+ lastModified: meta.updated ? new Date(String(meta.updated)) : undefined,
92
+ metadata: meta.metadata
93
+ };
94
+ }
95
+ export {
96
+ GoogleCloudStorageProvider
97
+ };