@contractspec/integration.providers-impls 1.57.0 → 1.59.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.
- package/dist/analytics.d.ts +1 -8
- package/dist/analytics.d.ts.map +1 -1
- package/dist/analytics.js +3 -3
- package/dist/calendar.d.ts +1 -8
- package/dist/calendar.d.ts.map +1 -1
- package/dist/calendar.js +3 -3
- package/dist/database.d.ts +1 -8
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +3 -3
- package/dist/email.d.ts +1 -8
- package/dist/email.d.ts.map +1 -1
- package/dist/email.js +3 -3
- package/dist/embedding.d.ts +1 -8
- package/dist/embedding.d.ts.map +1 -1
- package/dist/embedding.js +3 -3
- package/dist/impls/elevenlabs-voice.d.ts +14 -18
- package/dist/impls/elevenlabs-voice.d.ts.map +1 -1
- package/dist/impls/elevenlabs-voice.js +98 -88
- package/dist/impls/fal-voice.d.ts +22 -26
- package/dist/impls/fal-voice.d.ts.map +1 -1
- package/dist/impls/fal-voice.js +103 -78
- package/dist/impls/fathom-meeting-recorder.d.ts +35 -39
- package/dist/impls/fathom-meeting-recorder.d.ts.map +1 -1
- package/dist/impls/fathom-meeting-recorder.js +285 -142
- package/dist/impls/fathom-meeting-recorder.mapper.d.ts +4 -8
- package/dist/impls/fathom-meeting-recorder.mapper.d.ts.map +1 -1
- package/dist/impls/fathom-meeting-recorder.mapper.js +102 -38
- package/dist/impls/fathom-meeting-recorder.types.d.ts +16 -20
- package/dist/impls/fathom-meeting-recorder.types.d.ts.map +1 -1
- package/dist/impls/fathom-meeting-recorder.types.js +1 -0
- package/dist/impls/fathom-meeting-recorder.utils.d.ts +10 -14
- package/dist/impls/fathom-meeting-recorder.utils.d.ts.map +1 -1
- package/dist/impls/fathom-meeting-recorder.utils.js +58 -41
- package/dist/impls/fathom-meeting-recorder.webhooks.d.ts +3 -7
- package/dist/impls/fathom-meeting-recorder.webhooks.d.ts.map +1 -1
- package/dist/impls/fathom-meeting-recorder.webhooks.js +25 -20
- package/dist/impls/fireflies-meeting-recorder.d.ts +21 -25
- package/dist/impls/fireflies-meeting-recorder.d.ts.map +1 -1
- package/dist/impls/fireflies-meeting-recorder.js +272 -149
- package/dist/impls/fireflies-meeting-recorder.queries.d.ts +3 -6
- package/dist/impls/fireflies-meeting-recorder.queries.d.ts.map +1 -1
- package/dist/impls/fireflies-meeting-recorder.queries.js +10 -8
- package/dist/impls/fireflies-meeting-recorder.types.d.ts +26 -29
- package/dist/impls/fireflies-meeting-recorder.types.d.ts.map +1 -1
- package/dist/impls/fireflies-meeting-recorder.types.js +1 -0
- package/dist/impls/fireflies-meeting-recorder.utils.d.ts +4 -7
- package/dist/impls/fireflies-meeting-recorder.utils.d.ts.map +1 -1
- package/dist/impls/fireflies-meeting-recorder.utils.js +34 -27
- package/dist/impls/gcs-storage.d.ts +18 -22
- package/dist/impls/gcs-storage.d.ts.map +1 -1
- package/dist/impls/gcs-storage.js +92 -84
- package/dist/impls/gmail-inbound.d.ts +20 -24
- package/dist/impls/gmail-inbound.d.ts.map +1 -1
- package/dist/impls/gmail-inbound.js +212 -185
- package/dist/impls/gmail-outbound.d.ts +12 -16
- package/dist/impls/gmail-outbound.d.ts.map +1 -1
- package/dist/impls/gmail-outbound.js +126 -92
- package/dist/impls/google-calendar.d.ts +17 -21
- package/dist/impls/google-calendar.d.ts.map +1 -1
- package/dist/impls/google-calendar.js +182 -145
- package/dist/impls/gradium-voice.d.ts +20 -22
- package/dist/impls/gradium-voice.d.ts.map +1 -1
- package/dist/impls/gradium-voice.js +85 -74
- package/dist/impls/granola-meeting-recorder.d.ts +31 -24
- package/dist/impls/granola-meeting-recorder.d.ts.map +1 -1
- package/dist/impls/granola-meeting-recorder.js +511 -143
- package/dist/impls/granola-meeting-recorder.mcp.d.ts +25 -0
- package/dist/impls/granola-meeting-recorder.mcp.d.ts.map +1 -0
- package/dist/impls/granola-meeting-recorder.mcp.js +279 -0
- package/dist/impls/granola-meeting-recorder.types.d.ts +60 -49
- package/dist/impls/granola-meeting-recorder.types.d.ts.map +1 -1
- package/dist/impls/granola-meeting-recorder.types.js +1 -0
- package/dist/impls/index.d.ts +28 -28
- package/dist/impls/index.d.ts.map +1 -0
- package/dist/impls/index.js +4659 -29
- package/dist/impls/jira.d.ts +18 -22
- package/dist/impls/jira.d.ts.map +1 -1
- package/dist/impls/jira.js +112 -101
- package/dist/impls/linear.d.ts +17 -21
- package/dist/impls/linear.d.ts.map +1 -1
- package/dist/impls/linear.js +78 -69
- package/dist/impls/mistral-embedding.d.ts +17 -21
- package/dist/impls/mistral-embedding.d.ts.map +1 -1
- package/dist/impls/mistral-embedding.js +41 -39
- package/dist/impls/mistral-llm.d.ts +25 -29
- package/dist/impls/mistral-llm.d.ts.map +1 -1
- package/dist/impls/mistral-llm.js +266 -244
- package/dist/impls/notion.d.ts +20 -24
- package/dist/impls/notion.d.ts.map +1 -1
- package/dist/impls/notion.js +145 -110
- package/dist/impls/posthog-reader.d.ts +18 -22
- package/dist/impls/posthog-reader.d.ts.map +1 -1
- package/dist/impls/posthog-reader.js +148 -129
- package/dist/impls/posthog-utils.d.ts +4 -7
- package/dist/impls/posthog-utils.d.ts.map +1 -1
- package/dist/impls/posthog-utils.js +31 -22
- package/dist/impls/posthog.d.ts +33 -37
- package/dist/impls/posthog.d.ts.map +1 -1
- package/dist/impls/posthog.js +320 -119
- package/dist/impls/postmark-email.d.ts +13 -17
- package/dist/impls/postmark-email.d.ts.map +1 -1
- package/dist/impls/postmark-email.js +55 -50
- package/dist/impls/powens-client.d.ts +111 -114
- package/dist/impls/powens-client.d.ts.map +1 -1
- package/dist/impls/powens-client.js +194 -170
- package/dist/impls/powens-openbanking.d.ts +22 -26
- package/dist/impls/powens-openbanking.d.ts.map +1 -1
- package/dist/impls/powens-openbanking.js +425 -217
- package/dist/impls/provider-factory.d.ts +29 -33
- package/dist/impls/provider-factory.d.ts.map +1 -1
- package/dist/impls/provider-factory.js +4072 -275
- package/dist/impls/qdrant-vector.d.ts +18 -22
- package/dist/impls/qdrant-vector.d.ts.map +1 -1
- package/dist/impls/qdrant-vector.js +76 -69
- package/dist/impls/stripe-payments.d.ts +22 -26
- package/dist/impls/stripe-payments.d.ts.map +1 -1
- package/dist/impls/stripe-payments.js +219 -193
- package/dist/impls/supabase-psql.d.ts +21 -25
- package/dist/impls/supabase-psql.d.ts.map +1 -1
- package/dist/impls/supabase-psql.js +138 -98
- package/dist/impls/supabase-vector.d.ts +29 -33
- package/dist/impls/supabase-vector.d.ts.map +1 -1
- package/dist/impls/supabase-vector.js +278 -103
- package/dist/impls/tldv-meeting-recorder.d.ts +18 -22
- package/dist/impls/tldv-meeting-recorder.d.ts.map +1 -1
- package/dist/impls/tldv-meeting-recorder.js +142 -127
- package/dist/impls/twilio-sms.d.ts +14 -17
- package/dist/impls/twilio-sms.d.ts.map +1 -1
- package/dist/impls/twilio-sms.js +62 -55
- package/dist/index.d.ts +15 -64
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4700 -107
- package/dist/llm.d.ts +1 -8
- package/dist/llm.d.ts.map +1 -1
- package/dist/llm.js +3 -3
- package/dist/meeting-recorder.d.ts +1 -8
- package/dist/meeting-recorder.d.ts.map +1 -1
- package/dist/meeting-recorder.js +3 -3
- package/dist/node/analytics.js +2 -0
- package/dist/node/calendar.js +2 -0
- package/dist/node/database.js +2 -0
- package/dist/node/email.js +2 -0
- package/dist/node/embedding.js +2 -0
- package/dist/node/impls/elevenlabs-voice.js +102 -0
- package/dist/node/impls/fal-voice.js +112 -0
- package/dist/node/impls/fathom-meeting-recorder.js +287 -0
- package/dist/node/impls/fathom-meeting-recorder.mapper.js +105 -0
- package/dist/node/impls/fathom-meeting-recorder.types.js +0 -0
- package/dist/node/impls/fathom-meeting-recorder.utils.js +72 -0
- package/dist/node/impls/fathom-meeting-recorder.webhooks.js +29 -0
- package/dist/node/impls/fireflies-meeting-recorder.js +274 -0
- package/dist/node/impls/fireflies-meeting-recorder.queries.js +85 -0
- package/dist/node/impls/fireflies-meeting-recorder.types.js +0 -0
- package/dist/node/impls/fireflies-meeting-recorder.utils.js +42 -0
- package/dist/node/impls/gcs-storage.js +97 -0
- package/dist/node/impls/gmail-inbound.js +227 -0
- package/dist/node/impls/gmail-outbound.js +139 -0
- package/dist/node/impls/google-calendar.js +191 -0
- package/dist/node/impls/gradium-voice.js +90 -0
- package/dist/node/impls/granola-meeting-recorder.js +512 -0
- package/dist/node/impls/granola-meeting-recorder.mcp.js +278 -0
- package/dist/node/impls/granola-meeting-recorder.types.js +0 -0
- package/dist/node/impls/index.js +4658 -0
- package/dist/node/impls/jira.js +124 -0
- package/dist/node/impls/linear.js +83 -0
- package/dist/node/impls/mistral-embedding.js +43 -0
- package/dist/node/impls/mistral-llm.js +269 -0
- package/dist/node/impls/notion.js +160 -0
- package/dist/node/impls/posthog-reader.js +159 -0
- package/dist/node/impls/posthog-utils.js +38 -0
- package/dist/node/impls/posthog.js +322 -0
- package/dist/node/impls/postmark-email.js +60 -0
- package/dist/node/impls/powens-client.js +195 -0
- package/dist/node/impls/powens-openbanking.js +426 -0
- package/dist/node/impls/provider-factory.js +4080 -0
- package/dist/node/impls/qdrant-vector.js +78 -0
- package/dist/node/impls/stripe-payments.js +228 -0
- package/dist/node/impls/supabase-psql.js +150 -0
- package/dist/node/impls/supabase-vector.js +323 -0
- package/dist/node/impls/tldv-meeting-recorder.js +145 -0
- package/dist/node/impls/twilio-sms.js +65 -0
- package/dist/node/index.js +4699 -0
- package/dist/node/llm.js +2 -0
- package/dist/node/meeting-recorder.js +2 -0
- package/dist/node/openbanking.js +2 -0
- package/dist/node/payments.js +2 -0
- package/dist/node/project-management.js +2 -0
- package/dist/node/runtime.js +0 -0
- package/dist/node/secrets/provider.js +11 -0
- package/dist/node/sms.js +2 -0
- package/dist/node/storage.js +2 -0
- package/dist/node/vector-store.js +2 -0
- package/dist/node/voice.js +2 -0
- package/dist/openbanking.d.ts +1 -8
- package/dist/openbanking.d.ts.map +1 -1
- package/dist/openbanking.js +3 -3
- package/dist/payments.d.ts +1 -8
- package/dist/payments.d.ts.map +1 -1
- package/dist/payments.js +3 -3
- package/dist/project-management.d.ts +1 -8
- package/dist/project-management.d.ts.map +1 -1
- package/dist/project-management.js +3 -3
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +1 -0
- package/dist/secrets/provider.d.ts +3 -2
- package/dist/secrets/provider.d.ts.map +1 -0
- package/dist/secrets/provider.js +12 -3
- package/dist/sms.d.ts +1 -8
- package/dist/sms.d.ts.map +1 -1
- package/dist/sms.js +3 -3
- package/dist/storage.d.ts +1 -8
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +3 -3
- package/dist/vector-store.d.ts +1 -8
- package/dist/vector-store.d.ts.map +1 -1
- package/dist/vector-store.js +3 -3
- package/dist/voice.d.ts +1 -8
- package/dist/voice.d.ts.map +1 -1
- package/dist/voice.js +3 -3
- package/package.json +405 -114
- package/dist/_virtual/_rolldown/runtime.js +0 -36
- package/dist/impls/elevenlabs-voice.js.map +0 -1
- package/dist/impls/fal-voice.js.map +0 -1
- package/dist/impls/fathom-meeting-recorder.js.map +0 -1
- package/dist/impls/fathom-meeting-recorder.mapper.js.map +0 -1
- package/dist/impls/fathom-meeting-recorder.utils.js.map +0 -1
- package/dist/impls/fathom-meeting-recorder.webhooks.js.map +0 -1
- package/dist/impls/fireflies-meeting-recorder.js.map +0 -1
- package/dist/impls/fireflies-meeting-recorder.queries.js.map +0 -1
- package/dist/impls/fireflies-meeting-recorder.utils.js.map +0 -1
- package/dist/impls/gcs-storage.js.map +0 -1
- package/dist/impls/gmail-inbound.js.map +0 -1
- package/dist/impls/gmail-outbound.js.map +0 -1
- package/dist/impls/google-calendar.js.map +0 -1
- package/dist/impls/gradium-voice.js.map +0 -1
- package/dist/impls/granola-meeting-recorder.js.map +0 -1
- package/dist/impls/jira.js.map +0 -1
- package/dist/impls/linear.js.map +0 -1
- package/dist/impls/mistral-embedding.js.map +0 -1
- package/dist/impls/mistral-llm.js.map +0 -1
- package/dist/impls/notion.js.map +0 -1
- package/dist/impls/posthog-reader.js.map +0 -1
- package/dist/impls/posthog-utils.js.map +0 -1
- package/dist/impls/posthog.js.map +0 -1
- package/dist/impls/postmark-email.js.map +0 -1
- package/dist/impls/powens-client.js.map +0 -1
- package/dist/impls/powens-openbanking.js.map +0 -1
- package/dist/impls/provider-factory.js.map +0 -1
- package/dist/impls/qdrant-vector.js.map +0 -1
- package/dist/impls/stripe-payments.js.map +0 -1
- package/dist/impls/supabase-psql.js.map +0 -1
- package/dist/impls/supabase-vector.js.map +0 -1
- package/dist/impls/tldv-meeting-recorder.js.map +0 -1
- package/dist/impls/twilio-sms.js.map +0 -1
- 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
|
+
};
|
|
File without changes
|
|
@@ -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
|
+
};
|