@contractspec/integration.providers-impls 3.8.8 → 3.8.10
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.js +1 -2
- package/dist/calendar.js +1 -2
- package/dist/database.js +1 -2
- package/dist/email.js +1 -2
- package/dist/embedding.js +1 -2
- package/dist/health.js +1 -2
- package/dist/impls/async-event-queue.js +1 -48
- package/dist/impls/composio-fallback-resolver.js +1 -579
- package/dist/impls/composio-mcp.js +1 -163
- package/dist/impls/composio-proxies.js +1 -310
- package/dist/impls/composio-sdk.js +1 -77
- package/dist/impls/composio-types.js +1 -53
- package/dist/impls/elevenlabs-voice.js +1 -104
- package/dist/impls/fal-voice.js +1 -117
- package/dist/impls/fathom-meeting-recorder.js +2 -289
- package/dist/impls/fathom-meeting-recorder.mapper.js +1 -107
- package/dist/impls/fathom-meeting-recorder.utils.js +1 -74
- package/dist/impls/fathom-meeting-recorder.webhooks.js +1 -31
- package/dist/impls/fireflies-meeting-recorder.js +5 -203
- package/dist/impls/fireflies-meeting-recorder.queries.js +4 -14
- package/dist/impls/fireflies-meeting-recorder.utils.js +1 -44
- package/dist/impls/gcs-storage.js +1 -99
- package/dist/impls/gmail-inbound.js +1 -229
- package/dist/impls/gmail-outbound.js +25 -137
- package/dist/impls/google-calendar.js +1 -193
- package/dist/impls/gradium-voice.js +1 -95
- package/dist/impls/granola-meeting-recorder.js +3 -514
- package/dist/impls/granola-meeting-recorder.mcp.js +1 -280
- package/dist/impls/health/base-health-provider.js +1 -617
- package/dist/impls/health/hybrid-health-providers.js +1 -1089
- package/dist/impls/health/official-health-providers.js +1 -969
- package/dist/impls/health/provider-normalizers.js +1 -288
- package/dist/impls/health/providers.js +1 -1095
- package/dist/impls/health-provider-factory.js +1 -1309
- package/dist/impls/index.js +42 -7448
- package/dist/impls/jira.js +1 -126
- package/dist/impls/linear.js +1 -85
- package/dist/impls/messaging-github.js +1 -111
- package/dist/impls/messaging-slack.js +1 -81
- package/dist/impls/messaging-telegram.js +1 -48
- package/dist/impls/messaging-whatsapp-meta.js +1 -53
- package/dist/impls/messaging-whatsapp-twilio.js +1 -83
- package/dist/impls/mistral-conversational.js +2 -477
- package/dist/impls/mistral-conversational.session.js +2 -207
- package/dist/impls/mistral-embedding.js +1 -45
- package/dist/impls/mistral-llm.js +1 -271
- package/dist/impls/mistral-stt.js +1 -168
- package/dist/impls/notion.js +1 -162
- package/dist/impls/posthog-reader.js +1 -161
- package/dist/impls/posthog-utils.js +1 -40
- package/dist/impls/posthog.js +1 -324
- package/dist/impls/postmark-email.js +1 -62
- package/dist/impls/powens-client.js +1 -197
- package/dist/impls/powens-openbanking.js +1 -428
- package/dist/impls/provider-factory.js +18 -6268
- package/dist/impls/qdrant-vector.js +1 -80
- package/dist/impls/stripe-payments.js +1 -230
- package/dist/impls/supabase-psql.js +1 -152
- package/dist/impls/supabase-vector.js +9 -298
- package/dist/impls/tldv-meeting-recorder.js +2 -147
- package/dist/impls/twilio-sms.js +1 -67
- package/dist/index.js +42 -7495
- package/dist/llm.js +1 -2
- package/dist/meeting-recorder.js +1 -2
- package/dist/messaging.js +1 -2
- package/dist/node/analytics.js +1 -2
- package/dist/node/calendar.js +1 -2
- package/dist/node/database.js +1 -2
- package/dist/node/email.js +1 -2
- package/dist/node/embedding.js +1 -2
- package/dist/node/health.js +1 -2
- package/dist/node/impls/async-event-queue.js +1 -49
- package/dist/node/impls/composio-fallback-resolver.js +1 -580
- package/dist/node/impls/composio-mcp.js +1 -164
- package/dist/node/impls/composio-proxies.js +1 -311
- package/dist/node/impls/composio-sdk.js +1 -78
- package/dist/node/impls/composio-types.js +1 -54
- package/dist/node/impls/elevenlabs-voice.js +1 -105
- package/dist/node/impls/fal-voice.js +1 -118
- package/dist/node/impls/fathom-meeting-recorder.js +2 -290
- package/dist/node/impls/fathom-meeting-recorder.mapper.js +1 -108
- package/dist/node/impls/fathom-meeting-recorder.utils.js +1 -75
- package/dist/node/impls/fathom-meeting-recorder.webhooks.js +1 -32
- package/dist/node/impls/fireflies-meeting-recorder.js +5 -204
- package/dist/node/impls/fireflies-meeting-recorder.queries.js +4 -15
- package/dist/node/impls/fireflies-meeting-recorder.utils.js +1 -45
- package/dist/node/impls/gcs-storage.js +1 -100
- package/dist/node/impls/gmail-inbound.js +1 -230
- package/dist/node/impls/gmail-outbound.js +25 -138
- package/dist/node/impls/google-calendar.js +1 -194
- package/dist/node/impls/gradium-voice.js +1 -96
- package/dist/node/impls/granola-meeting-recorder.js +3 -515
- package/dist/node/impls/granola-meeting-recorder.mcp.js +1 -281
- package/dist/node/impls/health/base-health-provider.js +1 -618
- package/dist/node/impls/health/hybrid-health-providers.js +1 -1090
- package/dist/node/impls/health/official-health-providers.js +1 -970
- package/dist/node/impls/health/provider-normalizers.js +1 -289
- package/dist/node/impls/health/providers.js +1 -1096
- package/dist/node/impls/health-provider-factory.js +1 -1310
- package/dist/node/impls/index.js +42 -7449
- package/dist/node/impls/jira.js +1 -127
- package/dist/node/impls/linear.js +1 -86
- package/dist/node/impls/messaging-github.js +1 -112
- package/dist/node/impls/messaging-slack.js +1 -82
- package/dist/node/impls/messaging-telegram.js +1 -49
- package/dist/node/impls/messaging-whatsapp-meta.js +1 -54
- package/dist/node/impls/messaging-whatsapp-twilio.js +1 -84
- package/dist/node/impls/mistral-conversational.js +2 -478
- package/dist/node/impls/mistral-conversational.session.js +2 -208
- package/dist/node/impls/mistral-embedding.js +1 -46
- package/dist/node/impls/mistral-llm.js +1 -272
- package/dist/node/impls/mistral-stt.js +1 -169
- package/dist/node/impls/notion.js +1 -163
- package/dist/node/impls/posthog-reader.js +1 -162
- package/dist/node/impls/posthog-utils.js +1 -41
- package/dist/node/impls/posthog.js +1 -325
- package/dist/node/impls/postmark-email.js +1 -63
- package/dist/node/impls/powens-client.js +1 -198
- package/dist/node/impls/powens-openbanking.js +1 -429
- package/dist/node/impls/provider-factory.js +18 -6269
- package/dist/node/impls/qdrant-vector.js +1 -81
- package/dist/node/impls/stripe-payments.js +1 -231
- package/dist/node/impls/supabase-psql.js +1 -153
- package/dist/node/impls/supabase-vector.js +9 -299
- package/dist/node/impls/tldv-meeting-recorder.js +2 -148
- package/dist/node/impls/twilio-sms.js +1 -68
- package/dist/node/index.js +42 -7496
- package/dist/node/llm.js +1 -2
- package/dist/node/meeting-recorder.js +1 -2
- package/dist/node/messaging.js +1 -2
- package/dist/node/openbanking.js +1 -2
- package/dist/node/payments.js +1 -2
- package/dist/node/project-management.js +1 -2
- package/dist/node/secrets/provider.js +1 -14
- package/dist/node/sms.js +1 -2
- package/dist/node/storage.js +1 -2
- package/dist/node/vector-store.js +1 -2
- package/dist/node/voice.js +1 -2
- package/dist/openbanking.js +1 -2
- package/dist/payments.js +1 -2
- package/dist/project-management.js +1 -2
- package/dist/secrets/provider.js +1 -13
- package/dist/sms.js +1 -2
- package/dist/storage.js +1 -2
- package/dist/vector-store.js +1 -2
- package/dist/voice.js +1 -2
- package/package.json +17 -17
|
@@ -1,96 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
|
-
|
|
4
|
-
// src/impls/gradium-voice.ts
|
|
5
|
-
import { Gradium } from "@confiture-ai/gradium-sdk-js";
|
|
6
|
-
var FORMAT_MAP = {
|
|
7
|
-
mp3: "wav",
|
|
8
|
-
wav: "wav",
|
|
9
|
-
ogg: "opus",
|
|
10
|
-
pcm: "pcm",
|
|
11
|
-
opus: "opus"
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
class GradiumVoiceProvider {
|
|
15
|
-
client;
|
|
16
|
-
defaultVoiceId;
|
|
17
|
-
defaultOutputFormat;
|
|
18
|
-
constructor(options) {
|
|
19
|
-
this.client = options.client ?? new Gradium({
|
|
20
|
-
apiKey: options.apiKey,
|
|
21
|
-
region: options.region,
|
|
22
|
-
baseURL: options.baseUrl,
|
|
23
|
-
timeout: options.timeoutMs
|
|
24
|
-
});
|
|
25
|
-
this.defaultVoiceId = options.defaultVoiceId;
|
|
26
|
-
this.defaultOutputFormat = options.outputFormat;
|
|
27
|
-
}
|
|
28
|
-
async listVoices() {
|
|
29
|
-
const voices = await this.client.voices.list({ include_catalog: true });
|
|
30
|
-
return voices.map((voice) => this.fromGradiumVoice(voice));
|
|
31
|
-
}
|
|
32
|
-
async synthesize(input) {
|
|
33
|
-
const voiceId = input.voiceId ?? this.defaultVoiceId;
|
|
34
|
-
if (!voiceId) {
|
|
35
|
-
throw new Error("Voice ID is required for Gradium synthesis.");
|
|
36
|
-
}
|
|
37
|
-
const outputFormat = (input.format ? FORMAT_MAP[input.format] : undefined) ?? this.defaultOutputFormat ?? "wav";
|
|
38
|
-
const response = await this.client.tts.create({
|
|
39
|
-
voice_id: voiceId,
|
|
40
|
-
output_format: outputFormat,
|
|
41
|
-
text: input.text
|
|
42
|
-
});
|
|
43
|
-
const format = input.format ?? toContractFormat(outputFormat);
|
|
44
|
-
const sampleRate = input.sampleRateHz ?? response.sample_rate ?? inferSampleRate(outputFormat);
|
|
45
|
-
return {
|
|
46
|
-
audio: {
|
|
47
|
-
data: response.raw_data,
|
|
48
|
-
format,
|
|
49
|
-
sampleRateHz: sampleRate
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
fromGradiumVoice(voice) {
|
|
54
|
-
return {
|
|
55
|
-
id: voice.uid,
|
|
56
|
-
name: voice.name,
|
|
57
|
-
description: voice.description ?? undefined,
|
|
58
|
-
language: voice.language ?? undefined,
|
|
59
|
-
metadata: {
|
|
60
|
-
startSeconds: String(voice.start_s),
|
|
61
|
-
...voice.stop_s != null ? { stopSeconds: String(voice.stop_s) } : {},
|
|
62
|
-
filename: voice.filename
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
function toContractFormat(format) {
|
|
68
|
-
switch (format) {
|
|
69
|
-
case "opus":
|
|
70
|
-
return "opus";
|
|
71
|
-
case "wav":
|
|
72
|
-
return "wav";
|
|
73
|
-
case "pcm":
|
|
74
|
-
case "pcm_16000":
|
|
75
|
-
case "pcm_24000":
|
|
76
|
-
return "pcm";
|
|
77
|
-
default:
|
|
78
|
-
return format;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
function inferSampleRate(format) {
|
|
82
|
-
switch (format) {
|
|
83
|
-
case "ulaw_8000":
|
|
84
|
-
case "alaw_8000":
|
|
85
|
-
return 8000;
|
|
86
|
-
case "pcm_16000":
|
|
87
|
-
return 16000;
|
|
88
|
-
case "pcm_24000":
|
|
89
|
-
return 24000;
|
|
90
|
-
default:
|
|
91
|
-
return 48000;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
export {
|
|
95
|
-
GradiumVoiceProvider
|
|
96
|
-
};
|
|
1
|
+
import{createRequire as u}from"node:module";var l=u(import.meta.url);import{Gradium as s}from"@confiture-ai/gradium-sdk-js";var n={mp3:"wav",wav:"wav",ogg:"opus",pcm:"pcm",opus:"opus"};class m{client;defaultVoiceId;defaultOutputFormat;constructor(t){this.client=t.client??new s({apiKey:t.apiKey,region:t.region,baseURL:t.baseUrl,timeout:t.timeoutMs}),this.defaultVoiceId=t.defaultVoiceId,this.defaultOutputFormat=t.outputFormat}async listVoices(){return(await this.client.voices.list({include_catalog:!0})).map((e)=>this.fromGradiumVoice(e))}async synthesize(t){let e=t.voiceId??this.defaultVoiceId;if(!e)throw Error("Voice ID is required for Gradium synthesis.");let r=(t.format?n[t.format]:void 0)??this.defaultOutputFormat??"wav",a=await this.client.tts.create({voice_id:e,output_format:r,text:t.text}),i=t.format??c(r),o=t.sampleRateHz??a.sample_rate??d(r);return{audio:{data:a.raw_data,format:i,sampleRateHz:o}}}fromGradiumVoice(t){return{id:t.uid,name:t.name,description:t.description??void 0,language:t.language??void 0,metadata:{startSeconds:String(t.start_s),...t.stop_s!=null?{stopSeconds:String(t.stop_s)}:{},filename:t.filename}}}}function c(t){switch(t){case"opus":return"opus";case"wav":return"wav";case"pcm":case"pcm_16000":case"pcm_24000":return"pcm";default:return t}}function d(t){switch(t){case"ulaw_8000":case"alaw_8000":return 8000;case"pcm_16000":return 16000;case"pcm_24000":return 24000;default:return 48000}}export{m as GradiumVoiceProvider};
|
|
@@ -1,515 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
// src/impls/granola-meeting-recorder.mcp.ts
|
|
5
|
-
var UNKNOWN_EMAIL = "unknown@granola.local";
|
|
6
|
-
var EPOCH = "1970-01-01T00:00:00.000Z";
|
|
7
|
-
|
|
8
|
-
class GranolaMcpClient {
|
|
9
|
-
requestId = 0;
|
|
10
|
-
mcpUrl;
|
|
11
|
-
mcpAccessToken;
|
|
12
|
-
mcpHeaders;
|
|
13
|
-
fetchFn;
|
|
14
|
-
constructor(options) {
|
|
15
|
-
this.mcpUrl = options.mcpUrl;
|
|
16
|
-
this.mcpAccessToken = options.mcpAccessToken;
|
|
17
|
-
this.mcpHeaders = options.mcpHeaders;
|
|
18
|
-
this.fetchFn = options.fetchFn ?? fetch;
|
|
19
|
-
}
|
|
20
|
-
async callTool(name, args) {
|
|
21
|
-
const headers = {
|
|
22
|
-
"Content-Type": "application/json",
|
|
23
|
-
...this.mcpHeaders ?? {}
|
|
24
|
-
};
|
|
25
|
-
if (this.mcpAccessToken) {
|
|
26
|
-
headers.Authorization = `Bearer ${this.mcpAccessToken}`;
|
|
27
|
-
}
|
|
28
|
-
const response = await this.fetchFn(this.mcpUrl, {
|
|
29
|
-
method: "POST",
|
|
30
|
-
headers,
|
|
31
|
-
body: JSON.stringify({
|
|
32
|
-
jsonrpc: "2.0",
|
|
33
|
-
id: ++this.requestId,
|
|
34
|
-
method: "tools/call",
|
|
35
|
-
params: {
|
|
36
|
-
name,
|
|
37
|
-
arguments: args
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
});
|
|
41
|
-
if (!response.ok) {
|
|
42
|
-
const message = await safeReadText(response);
|
|
43
|
-
throw new Error(`Granola MCP error (${response.status}): ${message}`);
|
|
44
|
-
}
|
|
45
|
-
const rpc = await response.json();
|
|
46
|
-
if (rpc.error) {
|
|
47
|
-
throw new Error(rpc.error.message ?? "Granola MCP returned an error.");
|
|
48
|
-
}
|
|
49
|
-
return extractRpcResult(rpc);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
function normalizeMcpListResult(payload) {
|
|
53
|
-
const root = asObject(payload);
|
|
54
|
-
const list = asArray(payload) ?? asArray(root?.notes) ?? asArray(root?.meetings) ?? asArray(root?.items) ?? asArray(root?.results) ?? asArray(root?.data) ?? [];
|
|
55
|
-
const notes = list.map((item) => mapSummaryItem(item)).filter((item) => Boolean(item));
|
|
56
|
-
return {
|
|
57
|
-
notes,
|
|
58
|
-
nextCursor: readString(root, ["nextCursor", "next_cursor", "cursor"]),
|
|
59
|
-
hasMore: readBoolean(root, ["hasMore", "has_more"])
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
function normalizeMcpMeeting(payload, targetMeetingId) {
|
|
63
|
-
const root = asObject(payload);
|
|
64
|
-
const candidates = asArray(payload) ?? asArray(root?.meetings) ?? asArray(root?.notes) ?? asArray(root?.items) ?? asArray(root?.results) ?? asArray(root?.data);
|
|
65
|
-
if (candidates?.length) {
|
|
66
|
-
const selected = candidates.find((item) => readId(item) === targetMeetingId) ?? candidates.find((item) => String(readId(item) ?? "").includes(targetMeetingId)) ?? candidates[0];
|
|
67
|
-
return selected ? mapMeetingItem(selected) : undefined;
|
|
68
|
-
}
|
|
69
|
-
const direct = mapMeetingItem(payload);
|
|
70
|
-
if (direct && direct.id === targetMeetingId) {
|
|
71
|
-
return direct;
|
|
72
|
-
}
|
|
73
|
-
return direct;
|
|
74
|
-
}
|
|
75
|
-
function normalizeMcpTranscript(payload) {
|
|
76
|
-
const root = asObject(payload);
|
|
77
|
-
const list = asArray(payload) ?? asArray(root?.transcript) ?? asArray(root?.segments) ?? asArray(root?.items) ?? asArray(root?.data) ?? [];
|
|
78
|
-
if (list.length === 0 && typeof payload === "string") {
|
|
79
|
-
return [
|
|
80
|
-
{
|
|
81
|
-
text: payload,
|
|
82
|
-
start_time: "00:00:00",
|
|
83
|
-
end_time: "00:00:00"
|
|
84
|
-
}
|
|
85
|
-
];
|
|
86
|
-
}
|
|
87
|
-
return list.map((item) => mapTranscriptSegment(item)).filter((item) => Boolean(item));
|
|
88
|
-
}
|
|
89
|
-
function extractRpcResult(rpc) {
|
|
90
|
-
const result = rpc.result;
|
|
91
|
-
if (!result)
|
|
92
|
-
return null;
|
|
93
|
-
if (result.structuredContent !== undefined)
|
|
94
|
-
return result.structuredContent;
|
|
95
|
-
if (result.data !== undefined)
|
|
96
|
-
return result.data;
|
|
97
|
-
const textPayload = result.content?.find((entry) => entry?.type === "text")?.text;
|
|
98
|
-
if (!textPayload)
|
|
99
|
-
return result;
|
|
100
|
-
try {
|
|
101
|
-
return JSON.parse(textPayload);
|
|
102
|
-
} catch {
|
|
103
|
-
return textPayload;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
function mapSummaryItem(value) {
|
|
107
|
-
const object = asObject(value);
|
|
108
|
-
if (!object)
|
|
109
|
-
return;
|
|
110
|
-
const id = readId(object);
|
|
111
|
-
if (!id)
|
|
112
|
-
return;
|
|
113
|
-
const owner = mapOwner(object);
|
|
114
|
-
return {
|
|
115
|
-
id,
|
|
116
|
-
title: readString(object, ["title", "name", "meeting_title"]) ?? null,
|
|
117
|
-
owner,
|
|
118
|
-
created_at: readString(object, ["created_at", "createdAt", "date", "meeting_date"]) ?? EPOCH
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
function mapMeetingItem(value) {
|
|
122
|
-
const summary = mapSummaryItem(value);
|
|
123
|
-
if (!summary)
|
|
124
|
-
return;
|
|
125
|
-
const object = asObject(value) ?? {};
|
|
126
|
-
const attendees = asArray(object.attendees) ?? asArray(object.participants) ?? asArray(object.invitees) ?? [];
|
|
127
|
-
const mappedAttendees = attendees.map((entry) => mapUser(entry)).filter((entry) => Boolean(entry));
|
|
128
|
-
const folders = asArray(object.folder_membership) ?? asArray(object.folders) ?? [];
|
|
129
|
-
const folderMembership = folders.map((entry, index) => mapFolder(entry, index)).filter((entry) => Boolean(entry));
|
|
130
|
-
const calendarEvent = asObject(object.calendar_event) ? {
|
|
131
|
-
event_title: readString(asObject(object.calendar_event), [
|
|
132
|
-
"event_title",
|
|
133
|
-
"title"
|
|
134
|
-
]) ?? null,
|
|
135
|
-
invitees: mappedAttendees.map((attendee) => ({
|
|
136
|
-
email: attendee.email
|
|
137
|
-
})),
|
|
138
|
-
organiser: readString(asObject(object.calendar_event), [
|
|
139
|
-
"organiser",
|
|
140
|
-
"organizer"
|
|
141
|
-
]) ?? summary.owner.email,
|
|
142
|
-
calendar_event_id: readString(asObject(object.calendar_event), [
|
|
143
|
-
"calendar_event_id",
|
|
144
|
-
"id"
|
|
145
|
-
]) ?? null,
|
|
146
|
-
scheduled_start_time: readString(asObject(object.calendar_event), [
|
|
147
|
-
"scheduled_start_time",
|
|
148
|
-
"start_time",
|
|
149
|
-
"start"
|
|
150
|
-
]) ?? null,
|
|
151
|
-
scheduled_end_time: readString(asObject(object.calendar_event), [
|
|
152
|
-
"scheduled_end_time",
|
|
153
|
-
"end_time",
|
|
154
|
-
"end"
|
|
155
|
-
]) ?? null
|
|
156
|
-
} : null;
|
|
157
|
-
const transcript = normalizeMcpTranscript(object.transcript ?? object.segments);
|
|
158
|
-
return {
|
|
159
|
-
...summary,
|
|
160
|
-
calendar_event: calendarEvent,
|
|
161
|
-
attendees: mappedAttendees,
|
|
162
|
-
folder_membership: folderMembership,
|
|
163
|
-
summary_text: readString(object, ["summary_text", "summary", "enhanced_notes"]) ?? "",
|
|
164
|
-
transcript: transcript.length ? transcript : null
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
function mapTranscriptSegment(value) {
|
|
168
|
-
const object = asObject(value);
|
|
169
|
-
if (!object) {
|
|
170
|
-
if (typeof value !== "string")
|
|
171
|
-
return;
|
|
172
|
-
return {
|
|
173
|
-
text: value,
|
|
174
|
-
start_time: "00:00:00",
|
|
175
|
-
end_time: "00:00:00"
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
const text = readString(object, ["text", "content", "utterance"]);
|
|
179
|
-
if (!text)
|
|
180
|
-
return;
|
|
181
|
-
const speakerSource = readString(asObject(object.speaker), ["source", "name"]) ?? readString(object, ["speaker", "speaker_name"]);
|
|
182
|
-
return {
|
|
183
|
-
speaker: speakerSource ? { source: speakerSource } : undefined,
|
|
184
|
-
text,
|
|
185
|
-
start_time: readString(object, ["start_time", "startTime", "timestamp", "time"]) ?? "00:00:00",
|
|
186
|
-
end_time: readString(object, ["end_time", "endTime"]) ?? "00:00:00"
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
function mapOwner(object) {
|
|
190
|
-
const ownerObject = asObject(object.owner) ?? asObject(object.organizer) ?? asObject(object.organiser);
|
|
191
|
-
const attendee = asArray(object.attendees)?.[0] ?? asArray(object.participants)?.[0] ?? asArray(object.invitees)?.[0];
|
|
192
|
-
const ownerCandidate = ownerObject ?? asObject(attendee) ?? {};
|
|
193
|
-
return {
|
|
194
|
-
name: readString(ownerCandidate, ["name", "displayName"]) ?? null,
|
|
195
|
-
email: readString(ownerCandidate, ["email"]) ?? UNKNOWN_EMAIL
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
function mapUser(value) {
|
|
199
|
-
if (typeof value === "string") {
|
|
200
|
-
return {
|
|
201
|
-
name: null,
|
|
202
|
-
email: value
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
const object = asObject(value);
|
|
206
|
-
if (!object)
|
|
207
|
-
return;
|
|
208
|
-
const email = readString(object, ["email"]);
|
|
209
|
-
if (!email)
|
|
210
|
-
return;
|
|
211
|
-
return {
|
|
212
|
-
name: readString(object, ["name", "displayName"]) ?? null,
|
|
213
|
-
email
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
function mapFolder(value, index) {
|
|
217
|
-
const object = asObject(value);
|
|
218
|
-
if (!object)
|
|
219
|
-
return;
|
|
220
|
-
const id = readString(object, ["id"]) ?? `folder-${index}`;
|
|
221
|
-
const name = readString(object, ["name"]) ?? "Folder";
|
|
222
|
-
return {
|
|
223
|
-
id,
|
|
224
|
-
object: "folder",
|
|
225
|
-
name
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
function readId(value) {
|
|
229
|
-
const object = asObject(value);
|
|
230
|
-
if (!object)
|
|
231
|
-
return;
|
|
232
|
-
return readString(object, [
|
|
233
|
-
"id",
|
|
234
|
-
"meeting_id",
|
|
235
|
-
"meetingId",
|
|
236
|
-
"note_id",
|
|
237
|
-
"noteId"
|
|
238
|
-
]);
|
|
239
|
-
}
|
|
240
|
-
function readString(object, keys) {
|
|
241
|
-
if (!object)
|
|
242
|
-
return;
|
|
243
|
-
for (const key of keys) {
|
|
244
|
-
const value = object[key];
|
|
245
|
-
if (typeof value === "string" && value.trim().length > 0) {
|
|
246
|
-
return value;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
function readBoolean(object, keys) {
|
|
252
|
-
if (!object)
|
|
253
|
-
return;
|
|
254
|
-
for (const key of keys) {
|
|
255
|
-
const value = object[key];
|
|
256
|
-
if (typeof value === "boolean")
|
|
257
|
-
return value;
|
|
258
|
-
}
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
function asObject(value) {
|
|
262
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
263
|
-
return;
|
|
264
|
-
return value;
|
|
265
|
-
}
|
|
266
|
-
function asArray(value) {
|
|
267
|
-
return Array.isArray(value) ? value : undefined;
|
|
268
|
-
}
|
|
269
|
-
async function safeReadText(response) {
|
|
270
|
-
try {
|
|
271
|
-
return await response.text();
|
|
272
|
-
} catch {
|
|
273
|
-
return response.statusText;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// src/impls/granola-meeting-recorder.ts
|
|
278
|
-
var DEFAULT_BASE_URL = "https://public-api.granola.ai";
|
|
279
|
-
var DEFAULT_MCP_URL = "https://mcp.granola.ai/mcp";
|
|
280
|
-
var MAX_PAGE_SIZE = 30;
|
|
281
|
-
|
|
282
|
-
class GranolaMeetingRecorderProvider {
|
|
283
|
-
apiKey;
|
|
284
|
-
baseUrl;
|
|
285
|
-
defaultPageSize;
|
|
286
|
-
transport;
|
|
287
|
-
mcpClient;
|
|
288
|
-
constructor(options) {
|
|
289
|
-
this.apiKey = options.apiKey;
|
|
290
|
-
this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
|
|
291
|
-
this.defaultPageSize = options.pageSize;
|
|
292
|
-
this.transport = options.transport ?? "api";
|
|
293
|
-
this.mcpClient = new GranolaMcpClient({
|
|
294
|
-
mcpUrl: options.mcpUrl ?? DEFAULT_MCP_URL,
|
|
295
|
-
mcpAccessToken: options.mcpAccessToken,
|
|
296
|
-
mcpHeaders: options.mcpHeaders,
|
|
297
|
-
fetchFn: options.fetchFn
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
async listMeetings(params) {
|
|
301
|
-
if (this.transport === "mcp") {
|
|
302
|
-
return this.listMeetingsViaMcp(params);
|
|
303
|
-
}
|
|
304
|
-
const query = new URLSearchParams;
|
|
305
|
-
if (params.from)
|
|
306
|
-
query.set("created_after", params.from);
|
|
307
|
-
if (params.to)
|
|
308
|
-
query.set("created_before", params.to);
|
|
309
|
-
if (params.cursor)
|
|
310
|
-
query.set("cursor", params.cursor);
|
|
311
|
-
const pageSize = params.pageSize ?? this.defaultPageSize;
|
|
312
|
-
if (pageSize) {
|
|
313
|
-
query.set("page_size", String(Math.min(pageSize, MAX_PAGE_SIZE)));
|
|
314
|
-
}
|
|
315
|
-
const data = await this.request(`/v1/notes?${query.toString()}`);
|
|
316
|
-
return {
|
|
317
|
-
meetings: data.notes.map((note) => this.mapNoteSummary(note, params)),
|
|
318
|
-
nextCursor: data.cursor ?? undefined,
|
|
319
|
-
hasMore: data.hasMore
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
async getMeeting(params) {
|
|
323
|
-
if (this.transport === "mcp") {
|
|
324
|
-
return this.getMeetingViaMcp(params);
|
|
325
|
-
}
|
|
326
|
-
const includeTranscript = params.includeTranscript ?? false;
|
|
327
|
-
const note = await this.getNote(params.meetingId, includeTranscript);
|
|
328
|
-
return this.mapNoteDetail(note, params);
|
|
329
|
-
}
|
|
330
|
-
async getTranscript(params) {
|
|
331
|
-
if (this.transport === "mcp") {
|
|
332
|
-
return this.getTranscriptViaMcp(params);
|
|
333
|
-
}
|
|
334
|
-
const note = await this.getNote(params.meetingId, true);
|
|
335
|
-
const segments = this.mapTranscriptSegments(note.transcript);
|
|
336
|
-
return {
|
|
337
|
-
id: note.id,
|
|
338
|
-
meetingId: note.id,
|
|
339
|
-
tenantId: params.tenantId,
|
|
340
|
-
connectionId: params.connectionId ?? "unknown",
|
|
341
|
-
externalId: note.id,
|
|
342
|
-
format: "segments",
|
|
343
|
-
text: segments.map((segment) => segment.text).join(`
|
|
344
|
-
`),
|
|
345
|
-
segments,
|
|
346
|
-
generatedAt: note.created_at,
|
|
347
|
-
metadata: {
|
|
348
|
-
summaryText: note.summary_text
|
|
349
|
-
},
|
|
350
|
-
raw: note.transcript ?? undefined
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
async listMeetingsViaMcp(params) {
|
|
354
|
-
const payload = await this.mcpClient.callTool("list_meetings", {
|
|
355
|
-
cursor: params.cursor,
|
|
356
|
-
limit: params.pageSize ?? this.defaultPageSize,
|
|
357
|
-
query: params.query,
|
|
358
|
-
from: params.from,
|
|
359
|
-
to: params.to,
|
|
360
|
-
organizerEmail: params.organizerEmail,
|
|
361
|
-
participantEmail: params.participantEmail
|
|
362
|
-
});
|
|
363
|
-
const normalized = normalizeMcpListResult(payload);
|
|
364
|
-
return {
|
|
365
|
-
meetings: normalized.notes.map((note) => this.mapNoteSummary(note, params)),
|
|
366
|
-
nextCursor: normalized.nextCursor,
|
|
367
|
-
hasMore: normalized.hasMore ?? Boolean(normalized.nextCursor && normalized.notes.length > 0)
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
async getMeetingViaMcp(params) {
|
|
371
|
-
const primaryPayload = await this.mcpClient.callTool("list_meetings", {
|
|
372
|
-
query: params.meetingId,
|
|
373
|
-
limit: 50
|
|
374
|
-
});
|
|
375
|
-
let note = normalizeMcpMeeting(primaryPayload, params.meetingId);
|
|
376
|
-
if (!note) {
|
|
377
|
-
const fallbackPayload = await this.mcpClient.callTool("get_meetings", {
|
|
378
|
-
query: params.meetingId
|
|
379
|
-
});
|
|
380
|
-
note = normalizeMcpMeeting(fallbackPayload, params.meetingId);
|
|
381
|
-
}
|
|
382
|
-
if (!note) {
|
|
383
|
-
throw new Error(`Granola meeting "${params.meetingId}" not found via MCP.`);
|
|
384
|
-
}
|
|
385
|
-
return this.mapNoteDetail(note, params);
|
|
386
|
-
}
|
|
387
|
-
async getTranscriptViaMcp(params) {
|
|
388
|
-
const payload = await this.mcpClient.callTool("get_meeting_transcript", {
|
|
389
|
-
meeting_id: params.meetingId,
|
|
390
|
-
meetingId: params.meetingId,
|
|
391
|
-
id: params.meetingId
|
|
392
|
-
});
|
|
393
|
-
const transcript = normalizeMcpTranscript(payload);
|
|
394
|
-
const segments = this.mapTranscriptSegments(transcript);
|
|
395
|
-
return {
|
|
396
|
-
id: params.meetingId,
|
|
397
|
-
meetingId: params.meetingId,
|
|
398
|
-
tenantId: params.tenantId,
|
|
399
|
-
connectionId: params.connectionId ?? "unknown",
|
|
400
|
-
externalId: params.meetingId,
|
|
401
|
-
format: "segments",
|
|
402
|
-
text: segments.map((segment) => segment.text).join(`
|
|
403
|
-
`),
|
|
404
|
-
segments,
|
|
405
|
-
metadata: {
|
|
406
|
-
provider: "granola",
|
|
407
|
-
transport: "mcp"
|
|
408
|
-
},
|
|
409
|
-
raw: payload
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
async getNote(noteId, includeTranscript) {
|
|
413
|
-
const query = includeTranscript ? "?include=transcript" : "";
|
|
414
|
-
return this.request(`/v1/notes/${noteId}${query}`);
|
|
415
|
-
}
|
|
416
|
-
mapNoteSummary(note, params) {
|
|
417
|
-
const connectionId = params.connectionId ?? "unknown";
|
|
418
|
-
return {
|
|
419
|
-
id: note.id,
|
|
420
|
-
tenantId: params.tenantId,
|
|
421
|
-
connectionId,
|
|
422
|
-
externalId: note.id,
|
|
423
|
-
title: note.title ?? undefined,
|
|
424
|
-
organizer: this.mapUser(note.owner),
|
|
425
|
-
scheduledStartAt: note.created_at,
|
|
426
|
-
recordingStartAt: note.created_at,
|
|
427
|
-
transcriptAvailable: false,
|
|
428
|
-
createdAt: note.created_at,
|
|
429
|
-
updatedAt: note.created_at,
|
|
430
|
-
sourcePlatform: "granola"
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
mapNoteDetail(note, params) {
|
|
434
|
-
const connectionId = params.connectionId ?? "unknown";
|
|
435
|
-
const calendarEvent = note.calendar_event ?? undefined;
|
|
436
|
-
const invitees = calendarEvent?.invitees ? calendarEvent.invitees.map((invitee) => this.mapInvitee(invitee)) : note.attendees?.map((attendee) => this.mapUser(attendee)).filter(Boolean);
|
|
437
|
-
const participants = note.attendees?.map((attendee) => this.mapUser(attendee)).filter(Boolean);
|
|
438
|
-
return {
|
|
439
|
-
id: note.id,
|
|
440
|
-
tenantId: params.tenantId,
|
|
441
|
-
connectionId,
|
|
442
|
-
externalId: note.id,
|
|
443
|
-
title: note.title ?? calendarEvent?.event_title ?? undefined,
|
|
444
|
-
summary: note.summary_text ?? undefined,
|
|
445
|
-
organizer: this.mapUser(note.owner),
|
|
446
|
-
invitees: invitees?.length ? invitees : undefined,
|
|
447
|
-
participants: participants?.length ? participants : undefined,
|
|
448
|
-
scheduledStartAt: calendarEvent?.scheduled_start_time ?? undefined,
|
|
449
|
-
scheduledEndAt: calendarEvent?.scheduled_end_time ?? undefined,
|
|
450
|
-
recordingStartAt: calendarEvent?.scheduled_start_time ?? note.created_at,
|
|
451
|
-
recordingEndAt: calendarEvent?.scheduled_end_time ?? undefined,
|
|
452
|
-
transcriptAvailable: Array.isArray(note.transcript),
|
|
453
|
-
createdAt: note.created_at,
|
|
454
|
-
updatedAt: note.created_at,
|
|
455
|
-
sourcePlatform: "granola",
|
|
456
|
-
metadata: {
|
|
457
|
-
calendarEvent,
|
|
458
|
-
folderMembership: note.folder_membership
|
|
459
|
-
}
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
mapTranscriptSegments(transcript) {
|
|
463
|
-
if (!transcript)
|
|
464
|
-
return [];
|
|
465
|
-
return transcript.map((segment, index) => ({
|
|
466
|
-
index,
|
|
467
|
-
speakerName: segment.speaker?.source ?? undefined,
|
|
468
|
-
text: segment.text,
|
|
469
|
-
startTime: segment.start_time,
|
|
470
|
-
endTime: segment.end_time
|
|
471
|
-
}));
|
|
472
|
-
}
|
|
473
|
-
mapUser(user) {
|
|
474
|
-
if (!user)
|
|
475
|
-
return;
|
|
476
|
-
return {
|
|
477
|
-
name: user.name ?? undefined,
|
|
478
|
-
email: user.email ?? undefined,
|
|
479
|
-
role: "organizer"
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
mapInvitee(invitee) {
|
|
483
|
-
return {
|
|
484
|
-
email: invitee.email,
|
|
485
|
-
role: "attendee"
|
|
486
|
-
};
|
|
487
|
-
}
|
|
488
|
-
async request(path) {
|
|
489
|
-
if (!this.apiKey) {
|
|
490
|
-
throw new Error('Granola apiKey is required when transport is "api".');
|
|
491
|
-
}
|
|
492
|
-
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
493
|
-
headers: {
|
|
494
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
495
|
-
"Content-Type": "application/json"
|
|
496
|
-
}
|
|
497
|
-
});
|
|
498
|
-
if (!response.ok) {
|
|
499
|
-
const message = await safeReadError(response);
|
|
500
|
-
throw new Error(`Granola API error (${response.status}): ${message}`);
|
|
501
|
-
}
|
|
502
|
-
return await response.json();
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
async function safeReadError(response) {
|
|
506
|
-
try {
|
|
507
|
-
const data = await response.json();
|
|
508
|
-
return data?.message ?? response.statusText;
|
|
509
|
-
} catch {
|
|
510
|
-
return response.statusText;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
export {
|
|
514
|
-
GranolaMeetingRecorderProvider
|
|
515
|
-
};
|
|
1
|
+
import{createRequire as R}from"node:module";var z=R(import.meta.url);class u{requestId=0;mcpUrl;mcpAccessToken;mcpHeaders;fetchFn;constructor(e){this.mcpUrl=e.mcpUrl,this.mcpAccessToken=e.mcpAccessToken,this.mcpHeaders=e.mcpHeaders,this.fetchFn=e.fetchFn??fetch}async callTool(e,t){let n={"Content-Type":"application/json",...this.mcpHeaders??{}};if(this.mcpAccessToken)n.Authorization=`Bearer ${this.mcpAccessToken}`;let r=await this.fetchFn(this.mcpUrl,{method:"POST",headers:n,body:JSON.stringify({jsonrpc:"2.0",id:++this.requestId,method:"tools/call",params:{name:e,arguments:t}})});if(!r.ok){let l=await S(r);throw Error(`Granola MCP error (${r.status}): ${l}`)}let a=await r.json();if(a.error)throw Error(a.error.message??"Granola MCP returned an error.");return G(a)}}function M(e){let t=o(e);return{notes:(i(e)??i(t?.notes)??i(t?.meetings)??i(t?.items)??i(t?.results)??i(t?.data)??[]).map((a)=>y(a)).filter((a)=>Boolean(a)),nextCursor:s(t,["nextCursor","next_cursor","cursor"]),hasMore:k(t,["hasMore","has_more"])}}function p(e,t){let n=o(e),r=i(e)??i(n?.meetings)??i(n?.notes)??i(n?.items)??i(n?.results)??i(n?.data);if(r?.length){let l=r.find((c)=>m(c)===t)??r.find((c)=>String(m(c)??"").includes(t))??r[0];return l?h(l):void 0}let a=h(e);if(a&&a.id===t)return a;return a}function g(e){let t=o(e),n=i(e)??i(t?.transcript)??i(t?.segments)??i(t?.items)??i(t?.data)??[];if(n.length===0&&typeof e==="string")return[{text:e,start_time:"00:00:00",end_time:"00:00:00"}];return n.map((r)=>T(r)).filter((r)=>Boolean(r))}function G(e){let t=e.result;if(!t)return null;if(t.structuredContent!==void 0)return t.structuredContent;if(t.data!==void 0)return t.data;let n=t.content?.find((r)=>r?.type==="text")?.text;if(!n)return t;try{return JSON.parse(n)}catch{return n}}function y(e){let t=o(e);if(!t)return;let n=m(t);if(!n)return;let r=v(t);return{id:n,title:s(t,["title","name","meeting_title"])??null,owner:r,created_at:s(t,["created_at","createdAt","date","meeting_date"])??"1970-01-01T00:00:00.000Z"}}function h(e){let t=y(e);if(!t)return;let n=o(e)??{},a=(i(n.attendees)??i(n.participants)??i(n.invitees)??[]).map((d)=>P(d)).filter((d)=>Boolean(d)),c=(i(n.folder_membership)??i(n.folders)??[]).map((d,w)=>I(d,w)).filter((d)=>Boolean(d)),_=o(n.calendar_event)?{event_title:s(o(n.calendar_event),["event_title","title"])??null,invitees:a.map((d)=>({email:d.email})),organiser:s(o(n.calendar_event),["organiser","organizer"])??t.owner.email,calendar_event_id:s(o(n.calendar_event),["calendar_event_id","id"])??null,scheduled_start_time:s(o(n.calendar_event),["scheduled_start_time","start_time","start"])??null,scheduled_end_time:s(o(n.calendar_event),["scheduled_end_time","end_time","end"])??null}:null,f=g(n.transcript??n.segments);return{...t,calendar_event:_,attendees:a,folder_membership:c,summary_text:s(n,["summary_text","summary","enhanced_notes"])??"",transcript:f.length?f:null}}function T(e){let t=o(e);if(!t){if(typeof e!=="string")return;return{text:e,start_time:"00:00:00",end_time:"00:00:00"}}let n=s(t,["text","content","utterance"]);if(!n)return;let r=s(o(t.speaker),["source","name"])??s(t,["speaker","speaker_name"]);return{speaker:r?{source:r}:void 0,text:n,start_time:s(t,["start_time","startTime","timestamp","time"])??"00:00:00",end_time:s(t,["end_time","endTime"])??"00:00:00"}}function v(e){let t=o(e.owner)??o(e.organizer)??o(e.organiser),n=i(e.attendees)?.[0]??i(e.participants)?.[0]??i(e.invitees)?.[0],r=t??o(n)??{};return{name:s(r,["name","displayName"])??null,email:s(r,["email"])??"unknown@granola.local"}}function P(e){if(typeof e==="string")return{name:null,email:e};let t=o(e);if(!t)return;let n=s(t,["email"]);if(!n)return;return{name:s(t,["name","displayName"])??null,email:n}}function I(e,t){let n=o(e);if(!n)return;let r=s(n,["id"])??`folder-${t}`,a=s(n,["name"])??"Folder";return{id:r,object:"folder",name:a}}function m(e){let t=o(e);if(!t)return;return s(t,["id","meeting_id","meetingId","note_id","noteId"])}function s(e,t){if(!e)return;for(let n of t){let r=e[n];if(typeof r==="string"&&r.trim().length>0)return r}return}function k(e,t){if(!e)return;for(let n of t){let r=e[n];if(typeof r==="boolean")return r}return}function o(e){if(!e||typeof e!=="object"||Array.isArray(e))return;return e}function i(e){return Array.isArray(e)?e:void 0}async function S(e){try{return await e.text()}catch{return e.statusText}}var x="https://public-api.granola.ai",A="https://mcp.granola.ai/mcp",N=30;class U{apiKey;baseUrl;defaultPageSize;transport;mcpClient;constructor(e){this.apiKey=e.apiKey,this.baseUrl=e.baseUrl??x,this.defaultPageSize=e.pageSize,this.transport=e.transport??"api",this.mcpClient=new u({mcpUrl:e.mcpUrl??A,mcpAccessToken:e.mcpAccessToken,mcpHeaders:e.mcpHeaders,fetchFn:e.fetchFn})}async listMeetings(e){if(this.transport==="mcp")return this.listMeetingsViaMcp(e);let t=new URLSearchParams;if(e.from)t.set("created_after",e.from);if(e.to)t.set("created_before",e.to);if(e.cursor)t.set("cursor",e.cursor);let n=e.pageSize??this.defaultPageSize;if(n)t.set("page_size",String(Math.min(n,N)));let r=await this.request(`/v1/notes?${t.toString()}`);return{meetings:r.notes.map((a)=>this.mapNoteSummary(a,e)),nextCursor:r.cursor??void 0,hasMore:r.hasMore}}async getMeeting(e){if(this.transport==="mcp")return this.getMeetingViaMcp(e);let t=e.includeTranscript??!1,n=await this.getNote(e.meetingId,t);return this.mapNoteDetail(n,e)}async getTranscript(e){if(this.transport==="mcp")return this.getTranscriptViaMcp(e);let t=await this.getNote(e.meetingId,!0),n=this.mapTranscriptSegments(t.transcript);return{id:t.id,meetingId:t.id,tenantId:e.tenantId,connectionId:e.connectionId??"unknown",externalId:t.id,format:"segments",text:n.map((r)=>r.text).join(`
|
|
2
|
+
`),segments:n,generatedAt:t.created_at,metadata:{summaryText:t.summary_text},raw:t.transcript??void 0}}async listMeetingsViaMcp(e){let t=await this.mcpClient.callTool("list_meetings",{cursor:e.cursor,limit:e.pageSize??this.defaultPageSize,query:e.query,from:e.from,to:e.to,organizerEmail:e.organizerEmail,participantEmail:e.participantEmail}),n=M(t);return{meetings:n.notes.map((r)=>this.mapNoteSummary(r,e)),nextCursor:n.nextCursor,hasMore:n.hasMore??Boolean(n.nextCursor&&n.notes.length>0)}}async getMeetingViaMcp(e){let t=await this.mcpClient.callTool("list_meetings",{query:e.meetingId,limit:50}),n=p(t,e.meetingId);if(!n){let r=await this.mcpClient.callTool("get_meetings",{query:e.meetingId});n=p(r,e.meetingId)}if(!n)throw Error(`Granola meeting "${e.meetingId}" not found via MCP.`);return this.mapNoteDetail(n,e)}async getTranscriptViaMcp(e){let t=await this.mcpClient.callTool("get_meeting_transcript",{meeting_id:e.meetingId,meetingId:e.meetingId,id:e.meetingId}),n=g(t),r=this.mapTranscriptSegments(n);return{id:e.meetingId,meetingId:e.meetingId,tenantId:e.tenantId,connectionId:e.connectionId??"unknown",externalId:e.meetingId,format:"segments",text:r.map((a)=>a.text).join(`
|
|
3
|
+
`),segments:r,metadata:{provider:"granola",transport:"mcp"},raw:t}}async getNote(e,t){let n=t?"?include=transcript":"";return this.request(`/v1/notes/${e}${n}`)}mapNoteSummary(e,t){let n=t.connectionId??"unknown";return{id:e.id,tenantId:t.tenantId,connectionId:n,externalId:e.id,title:e.title??void 0,organizer:this.mapUser(e.owner),scheduledStartAt:e.created_at,recordingStartAt:e.created_at,transcriptAvailable:!1,createdAt:e.created_at,updatedAt:e.created_at,sourcePlatform:"granola"}}mapNoteDetail(e,t){let n=t.connectionId??"unknown",r=e.calendar_event??void 0,a=r?.invitees?r.invitees.map((c)=>this.mapInvitee(c)):e.attendees?.map((c)=>this.mapUser(c)).filter(Boolean),l=e.attendees?.map((c)=>this.mapUser(c)).filter(Boolean);return{id:e.id,tenantId:t.tenantId,connectionId:n,externalId:e.id,title:e.title??r?.event_title??void 0,summary:e.summary_text??void 0,organizer:this.mapUser(e.owner),invitees:a?.length?a:void 0,participants:l?.length?l:void 0,scheduledStartAt:r?.scheduled_start_time??void 0,scheduledEndAt:r?.scheduled_end_time??void 0,recordingStartAt:r?.scheduled_start_time??e.created_at,recordingEndAt:r?.scheduled_end_time??void 0,transcriptAvailable:Array.isArray(e.transcript),createdAt:e.created_at,updatedAt:e.created_at,sourcePlatform:"granola",metadata:{calendarEvent:r,folderMembership:e.folder_membership}}}mapTranscriptSegments(e){if(!e)return[];return e.map((t,n)=>({index:n,speakerName:t.speaker?.source??void 0,text:t.text,startTime:t.start_time,endTime:t.end_time}))}mapUser(e){if(!e)return;return{name:e.name??void 0,email:e.email??void 0,role:"organizer"}}mapInvitee(e){return{email:e.email,role:"attendee"}}async request(e){if(!this.apiKey)throw Error('Granola apiKey is required when transport is "api".');let t=await fetch(`${this.baseUrl}${e}`,{headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"}});if(!t.ok){let n=await C(t);throw Error(`Granola API error (${t.status}): ${n}`)}return await t.json()}}async function C(e){try{return(await e.json())?.message??e.statusText}catch{return e.statusText}}export{U as GranolaMeetingRecorderProvider};
|