@contractspec/integration.providers-impls 1.56.1 → 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 (238) hide show
  1. package/README.md +115 -1
  2. package/dist/analytics.d.ts +2 -0
  3. package/dist/analytics.d.ts.map +1 -0
  4. package/dist/analytics.js +3 -0
  5. package/dist/calendar.d.ts +1 -7
  6. package/dist/calendar.d.ts.map +1 -1
  7. package/dist/calendar.js +3 -3
  8. package/dist/database.d.ts +2 -0
  9. package/dist/database.d.ts.map +1 -0
  10. package/dist/database.js +3 -0
  11. package/dist/email.d.ts +1 -7
  12. package/dist/email.d.ts.map +1 -1
  13. package/dist/email.js +3 -3
  14. package/dist/embedding.d.ts +1 -7
  15. package/dist/embedding.d.ts.map +1 -1
  16. package/dist/embedding.js +3 -3
  17. package/dist/impls/elevenlabs-voice.d.ts +14 -18
  18. package/dist/impls/elevenlabs-voice.d.ts.map +1 -1
  19. package/dist/impls/elevenlabs-voice.js +98 -88
  20. package/dist/impls/fal-voice.d.ts +25 -0
  21. package/dist/impls/fal-voice.d.ts.map +1 -0
  22. package/dist/impls/fal-voice.js +113 -0
  23. package/dist/impls/fathom-meeting-recorder.d.ts +38 -0
  24. package/dist/impls/fathom-meeting-recorder.d.ts.map +1 -0
  25. package/dist/impls/fathom-meeting-recorder.js +288 -0
  26. package/dist/impls/fathom-meeting-recorder.mapper.d.ts +5 -0
  27. package/dist/impls/fathom-meeting-recorder.mapper.d.ts.map +1 -0
  28. package/dist/impls/fathom-meeting-recorder.mapper.js +106 -0
  29. package/dist/impls/fathom-meeting-recorder.types.d.ts +20 -0
  30. package/dist/impls/fathom-meeting-recorder.types.d.ts.map +1 -0
  31. package/dist/impls/fathom-meeting-recorder.types.js +1 -0
  32. package/dist/impls/fathom-meeting-recorder.utils.d.ts +11 -0
  33. package/dist/impls/fathom-meeting-recorder.utils.d.ts.map +1 -0
  34. package/dist/impls/fathom-meeting-recorder.utils.js +73 -0
  35. package/dist/impls/fathom-meeting-recorder.webhooks.d.ts +4 -0
  36. package/dist/impls/fathom-meeting-recorder.webhooks.d.ts.map +1 -0
  37. package/dist/impls/fathom-meeting-recorder.webhooks.js +30 -0
  38. package/dist/impls/fireflies-meeting-recorder.d.ts +24 -0
  39. package/dist/impls/fireflies-meeting-recorder.d.ts.map +1 -0
  40. package/dist/impls/fireflies-meeting-recorder.js +275 -0
  41. package/dist/impls/fireflies-meeting-recorder.queries.d.ts +4 -0
  42. package/dist/impls/fireflies-meeting-recorder.queries.d.ts.map +1 -0
  43. package/dist/impls/fireflies-meeting-recorder.queries.js +86 -0
  44. package/dist/impls/fireflies-meeting-recorder.types.d.ts +32 -0
  45. package/dist/impls/fireflies-meeting-recorder.types.d.ts.map +1 -0
  46. package/dist/impls/fireflies-meeting-recorder.types.js +1 -0
  47. package/dist/impls/fireflies-meeting-recorder.utils.d.ts +5 -0
  48. package/dist/impls/fireflies-meeting-recorder.utils.d.ts.map +1 -0
  49. package/dist/impls/fireflies-meeting-recorder.utils.js +43 -0
  50. package/dist/impls/gcs-storage.d.ts +18 -22
  51. package/dist/impls/gcs-storage.d.ts.map +1 -1
  52. package/dist/impls/gcs-storage.js +92 -84
  53. package/dist/impls/gmail-inbound.d.ts +20 -24
  54. package/dist/impls/gmail-inbound.d.ts.map +1 -1
  55. package/dist/impls/gmail-inbound.js +212 -185
  56. package/dist/impls/gmail-outbound.d.ts +12 -16
  57. package/dist/impls/gmail-outbound.d.ts.map +1 -1
  58. package/dist/impls/gmail-outbound.js +126 -92
  59. package/dist/impls/google-calendar.d.ts +17 -21
  60. package/dist/impls/google-calendar.d.ts.map +1 -1
  61. package/dist/impls/google-calendar.js +182 -145
  62. package/dist/impls/gradium-voice.d.ts +24 -0
  63. package/dist/impls/gradium-voice.d.ts.map +1 -0
  64. package/dist/impls/gradium-voice.js +91 -0
  65. package/dist/impls/granola-meeting-recorder.d.ts +34 -0
  66. package/dist/impls/granola-meeting-recorder.d.ts.map +1 -0
  67. package/dist/impls/granola-meeting-recorder.js +513 -0
  68. package/dist/impls/granola-meeting-recorder.mcp.d.ts +25 -0
  69. package/dist/impls/granola-meeting-recorder.mcp.d.ts.map +1 -0
  70. package/dist/impls/granola-meeting-recorder.mcp.js +279 -0
  71. package/dist/impls/granola-meeting-recorder.types.d.ts +61 -0
  72. package/dist/impls/granola-meeting-recorder.types.d.ts.map +1 -0
  73. package/dist/impls/granola-meeting-recorder.types.js +1 -0
  74. package/dist/impls/index.d.ts +28 -15
  75. package/dist/impls/index.d.ts.map +1 -0
  76. package/dist/impls/index.js +4659 -16
  77. package/dist/impls/jira.d.ts +21 -0
  78. package/dist/impls/jira.d.ts.map +1 -0
  79. package/dist/impls/jira.js +125 -0
  80. package/dist/impls/linear.d.ts +20 -0
  81. package/dist/impls/linear.d.ts.map +1 -0
  82. package/dist/impls/linear.js +84 -0
  83. package/dist/impls/mistral-embedding.d.ts +17 -21
  84. package/dist/impls/mistral-embedding.d.ts.map +1 -1
  85. package/dist/impls/mistral-embedding.js +41 -39
  86. package/dist/impls/mistral-llm.d.ts +25 -29
  87. package/dist/impls/mistral-llm.d.ts.map +1 -1
  88. package/dist/impls/mistral-llm.js +266 -244
  89. package/dist/impls/notion.d.ts +23 -0
  90. package/dist/impls/notion.d.ts.map +1 -0
  91. package/dist/impls/notion.js +161 -0
  92. package/dist/impls/posthog-reader.d.ts +22 -0
  93. package/dist/impls/posthog-reader.d.ts.map +1 -0
  94. package/dist/impls/posthog-reader.js +160 -0
  95. package/dist/impls/posthog-utils.d.ts +5 -0
  96. package/dist/impls/posthog-utils.d.ts.map +1 -0
  97. package/dist/impls/posthog-utils.js +39 -0
  98. package/dist/impls/posthog.d.ts +36 -0
  99. package/dist/impls/posthog.d.ts.map +1 -0
  100. package/dist/impls/posthog.js +323 -0
  101. package/dist/impls/postmark-email.d.ts +13 -17
  102. package/dist/impls/postmark-email.d.ts.map +1 -1
  103. package/dist/impls/postmark-email.js +55 -50
  104. package/dist/impls/powens-client.d.ts +111 -114
  105. package/dist/impls/powens-client.d.ts.map +1 -1
  106. package/dist/impls/powens-client.js +194 -170
  107. package/dist/impls/powens-openbanking.d.ts +22 -26
  108. package/dist/impls/powens-openbanking.d.ts.map +1 -1
  109. package/dist/impls/powens-openbanking.js +425 -217
  110. package/dist/impls/provider-factory.d.ts +29 -25
  111. package/dist/impls/provider-factory.d.ts.map +1 -1
  112. package/dist/impls/provider-factory.js +4074 -136
  113. package/dist/impls/qdrant-vector.d.ts +18 -22
  114. package/dist/impls/qdrant-vector.d.ts.map +1 -1
  115. package/dist/impls/qdrant-vector.js +76 -69
  116. package/dist/impls/stripe-payments.d.ts +22 -26
  117. package/dist/impls/stripe-payments.d.ts.map +1 -1
  118. package/dist/impls/stripe-payments.js +219 -193
  119. package/dist/impls/supabase-psql.d.ts +24 -0
  120. package/dist/impls/supabase-psql.d.ts.map +1 -0
  121. package/dist/impls/supabase-psql.js +151 -0
  122. package/dist/impls/supabase-vector.d.ts +32 -0
  123. package/dist/impls/supabase-vector.d.ts.map +1 -0
  124. package/dist/impls/supabase-vector.js +324 -0
  125. package/dist/impls/tldv-meeting-recorder.d.ts +21 -0
  126. package/dist/impls/tldv-meeting-recorder.d.ts.map +1 -0
  127. package/dist/impls/tldv-meeting-recorder.js +146 -0
  128. package/dist/impls/twilio-sms.d.ts +14 -17
  129. package/dist/impls/twilio-sms.d.ts.map +1 -1
  130. package/dist/impls/twilio-sms.js +62 -55
  131. package/dist/index.d.ts +15 -43
  132. package/dist/index.d.ts.map +1 -1
  133. package/dist/index.js +4700 -69
  134. package/dist/llm.d.ts +1 -7
  135. package/dist/llm.d.ts.map +1 -1
  136. package/dist/llm.js +3 -3
  137. package/dist/meeting-recorder.d.ts +2 -0
  138. package/dist/meeting-recorder.d.ts.map +1 -0
  139. package/dist/meeting-recorder.js +3 -0
  140. package/dist/node/analytics.js +2 -0
  141. package/dist/node/calendar.js +2 -0
  142. package/dist/node/database.js +2 -0
  143. package/dist/node/email.js +2 -0
  144. package/dist/node/embedding.js +2 -0
  145. package/dist/node/impls/elevenlabs-voice.js +102 -0
  146. package/dist/node/impls/fal-voice.js +112 -0
  147. package/dist/node/impls/fathom-meeting-recorder.js +287 -0
  148. package/dist/node/impls/fathom-meeting-recorder.mapper.js +105 -0
  149. package/dist/node/impls/fathom-meeting-recorder.types.js +0 -0
  150. package/dist/node/impls/fathom-meeting-recorder.utils.js +72 -0
  151. package/dist/node/impls/fathom-meeting-recorder.webhooks.js +29 -0
  152. package/dist/node/impls/fireflies-meeting-recorder.js +274 -0
  153. package/dist/node/impls/fireflies-meeting-recorder.queries.js +85 -0
  154. package/dist/node/impls/fireflies-meeting-recorder.types.js +0 -0
  155. package/dist/node/impls/fireflies-meeting-recorder.utils.js +42 -0
  156. package/dist/node/impls/gcs-storage.js +97 -0
  157. package/dist/node/impls/gmail-inbound.js +227 -0
  158. package/dist/node/impls/gmail-outbound.js +139 -0
  159. package/dist/node/impls/google-calendar.js +191 -0
  160. package/dist/node/impls/gradium-voice.js +90 -0
  161. package/dist/node/impls/granola-meeting-recorder.js +512 -0
  162. package/dist/node/impls/granola-meeting-recorder.mcp.js +278 -0
  163. package/dist/node/impls/granola-meeting-recorder.types.js +0 -0
  164. package/dist/node/impls/index.js +4658 -0
  165. package/dist/node/impls/jira.js +124 -0
  166. package/dist/node/impls/linear.js +83 -0
  167. package/dist/node/impls/mistral-embedding.js +43 -0
  168. package/dist/node/impls/mistral-llm.js +269 -0
  169. package/dist/node/impls/notion.js +160 -0
  170. package/dist/node/impls/posthog-reader.js +159 -0
  171. package/dist/node/impls/posthog-utils.js +38 -0
  172. package/dist/node/impls/posthog.js +322 -0
  173. package/dist/node/impls/postmark-email.js +60 -0
  174. package/dist/node/impls/powens-client.js +195 -0
  175. package/dist/node/impls/powens-openbanking.js +426 -0
  176. package/dist/node/impls/provider-factory.js +4080 -0
  177. package/dist/node/impls/qdrant-vector.js +78 -0
  178. package/dist/node/impls/stripe-payments.js +228 -0
  179. package/dist/node/impls/supabase-psql.js +150 -0
  180. package/dist/node/impls/supabase-vector.js +323 -0
  181. package/dist/node/impls/tldv-meeting-recorder.js +145 -0
  182. package/dist/node/impls/twilio-sms.js +65 -0
  183. package/dist/node/index.js +4699 -0
  184. package/dist/node/llm.js +2 -0
  185. package/dist/node/meeting-recorder.js +2 -0
  186. package/dist/node/openbanking.js +2 -0
  187. package/dist/node/payments.js +2 -0
  188. package/dist/node/project-management.js +2 -0
  189. package/dist/node/runtime.js +0 -0
  190. package/dist/node/secrets/provider.js +11 -0
  191. package/dist/node/sms.js +2 -0
  192. package/dist/node/storage.js +2 -0
  193. package/dist/node/vector-store.js +2 -0
  194. package/dist/node/voice.js +2 -0
  195. package/dist/openbanking.d.ts +1 -7
  196. package/dist/openbanking.d.ts.map +1 -1
  197. package/dist/openbanking.js +3 -3
  198. package/dist/payments.d.ts +1 -7
  199. package/dist/payments.d.ts.map +1 -1
  200. package/dist/payments.js +3 -3
  201. package/dist/project-management.d.ts +2 -0
  202. package/dist/project-management.d.ts.map +1 -0
  203. package/dist/project-management.js +3 -0
  204. package/dist/runtime.d.ts +2 -2
  205. package/dist/runtime.d.ts.map +1 -0
  206. package/dist/runtime.js +1 -0
  207. package/dist/secrets/provider.d.ts +3 -2
  208. package/dist/secrets/provider.d.ts.map +1 -0
  209. package/dist/secrets/provider.js +12 -3
  210. package/dist/sms.d.ts +1 -7
  211. package/dist/sms.d.ts.map +1 -1
  212. package/dist/sms.js +3 -3
  213. package/dist/storage.d.ts +1 -7
  214. package/dist/storage.d.ts.map +1 -1
  215. package/dist/storage.js +3 -3
  216. package/dist/vector-store.d.ts +1 -7
  217. package/dist/vector-store.d.ts.map +1 -1
  218. package/dist/vector-store.js +3 -3
  219. package/dist/voice.d.ts +1 -7
  220. package/dist/voice.d.ts.map +1 -1
  221. package/dist/voice.js +3 -3
  222. package/package.json +419 -76
  223. package/dist/_virtual/rolldown_runtime.js +0 -36
  224. package/dist/impls/elevenlabs-voice.js.map +0 -1
  225. package/dist/impls/gcs-storage.js.map +0 -1
  226. package/dist/impls/gmail-inbound.js.map +0 -1
  227. package/dist/impls/gmail-outbound.js.map +0 -1
  228. package/dist/impls/google-calendar.js.map +0 -1
  229. package/dist/impls/mistral-embedding.js.map +0 -1
  230. package/dist/impls/mistral-llm.js.map +0 -1
  231. package/dist/impls/postmark-email.js.map +0 -1
  232. package/dist/impls/powens-client.js.map +0 -1
  233. package/dist/impls/powens-openbanking.js.map +0 -1
  234. package/dist/impls/provider-factory.js.map +0 -1
  235. package/dist/impls/qdrant-vector.js.map +0 -1
  236. package/dist/impls/stripe-payments.js.map +0 -1
  237. package/dist/impls/twilio-sms.js.map +0 -1
  238. package/dist/index.js.map +0 -1
@@ -0,0 +1,227 @@
1
+ // src/impls/gmail-inbound.ts
2
+ import { google } from "googleapis";
3
+
4
+ class GmailInboundProvider {
5
+ gmail;
6
+ userId;
7
+ includeSpamTrash;
8
+ auth;
9
+ constructor(options) {
10
+ this.auth = options.auth;
11
+ this.gmail = options.gmail ?? google.gmail({
12
+ version: "v1",
13
+ auth: options.auth
14
+ });
15
+ this.userId = options.userId ?? "me";
16
+ this.includeSpamTrash = options.includeSpamTrash ?? false;
17
+ }
18
+ async listThreads(query) {
19
+ const response = await this.gmail.users.threads.list({
20
+ userId: this.userId,
21
+ maxResults: query?.pageSize,
22
+ pageToken: query?.pageToken,
23
+ q: query?.query,
24
+ labelIds: query?.label ? [query.label] : undefined,
25
+ includeSpamTrash: this.includeSpamTrash,
26
+ auth: this.auth
27
+ });
28
+ const threads = await Promise.all((response.data.threads ?? []).map(async (thread) => {
29
+ if (!thread.id)
30
+ return null;
31
+ return this.getThread(thread.id);
32
+ }));
33
+ return threads.filter((thread) => thread !== null);
34
+ }
35
+ async getThread(threadId) {
36
+ const response = await this.gmail.users.threads.get({
37
+ id: threadId,
38
+ userId: this.userId,
39
+ format: "full",
40
+ auth: this.auth
41
+ });
42
+ const thread = response.data;
43
+ if (!thread)
44
+ return null;
45
+ const messages = thread.messages?.map((message) => this.transformMessage(message)) ?? [];
46
+ const participants = dedupeAddresses(messages.flatMap((message) => [
47
+ message.from,
48
+ ...message.to,
49
+ ...message.cc ?? []
50
+ ]));
51
+ const firstMessage = messages[0];
52
+ const lastMessage = messages[messages.length - 1];
53
+ const updatedAt = lastMessage?.receivedAt ?? lastMessage?.sentAt ?? firstMessage?.receivedAt ?? firstMessage?.sentAt ?? new Date;
54
+ const labels = Array.from(new Set(messages.flatMap((message) => {
55
+ const labelField = message.metadata?.labelIds;
56
+ if (!labelField)
57
+ return [];
58
+ return labelField.split(",").map((label) => label.trim());
59
+ }).filter((label) => Boolean(label))));
60
+ return {
61
+ id: thread.id ?? threadId,
62
+ subject: messages[0]?.subject,
63
+ snippet: thread.snippet ?? "",
64
+ participants,
65
+ messages,
66
+ updatedAt,
67
+ labels,
68
+ metadata: thread.historyId ? { historyId: thread.historyId } : undefined
69
+ };
70
+ }
71
+ async listMessagesSince(query) {
72
+ const after = query.since ? Math.floor(query.since.getTime() / 1000) : undefined;
73
+ const q = [];
74
+ if (after) {
75
+ q.push(`after:${after}`);
76
+ }
77
+ const response = await this.gmail.users.messages.list({
78
+ userId: this.userId,
79
+ maxResults: query.pageSize,
80
+ pageToken: query.pageToken,
81
+ labelIds: query.label ? [query.label] : undefined,
82
+ q: q.join(" "),
83
+ includeSpamTrash: this.includeSpamTrash,
84
+ auth: this.auth
85
+ });
86
+ const messages = await Promise.all((response.data.messages ?? []).map(async (item) => {
87
+ if (!item.id)
88
+ return null;
89
+ const full = await this.gmail.users.messages.get({
90
+ userId: this.userId,
91
+ id: item.id,
92
+ format: "full",
93
+ auth: this.auth
94
+ });
95
+ if (!full.data)
96
+ return null;
97
+ return this.transformMessage(full.data);
98
+ }));
99
+ return {
100
+ messages: messages.filter((message) => message !== null),
101
+ nextPageToken: response.data.nextPageToken ?? undefined
102
+ };
103
+ }
104
+ transformMessage(message) {
105
+ const headers = message.payload?.headers ?? [];
106
+ const subject = headerValue(headers, "Subject") ?? "";
107
+ const from = parseAddress(headerValue(headers, "From")) ?? inferFallbackAddress("from", message.id);
108
+ const to = parseAddressList(headerValue(headers, "To"));
109
+ const cc = parseAddressList(headerValue(headers, "Cc"));
110
+ const bcc = parseAddressList(headerValue(headers, "Bcc"));
111
+ const replyTo = parseAddress(headerValue(headers, "Reply-To"));
112
+ const { text, html, attachments } = extractContent(message.payload);
113
+ const timestamp = message.internalDate ? new Date(Number(message.internalDate)) : new Date;
114
+ const metadata = {
115
+ ...message.labelIds?.length ? { labelIds: message.labelIds.join(",") } : {},
116
+ ...message.historyId ? { historyId: message.historyId } : {}
117
+ };
118
+ return {
119
+ id: message.id ?? "",
120
+ threadId: message.threadId ?? "",
121
+ subject,
122
+ from,
123
+ to,
124
+ cc,
125
+ bcc,
126
+ replyTo: replyTo ?? undefined,
127
+ sentAt: timestamp,
128
+ receivedAt: timestamp,
129
+ textBody: text ?? undefined,
130
+ htmlBody: html ?? undefined,
131
+ attachments,
132
+ headers: Object.fromEntries(headers.map((header) => [header.name ?? "", header.value ?? ""])),
133
+ metadata: Object.keys(metadata).length > 0 ? metadata : undefined
134
+ };
135
+ }
136
+ }
137
+ function headerValue(headers, name) {
138
+ const header = headers.find((candidate) => candidate.name?.toLowerCase() === name.toLowerCase());
139
+ const value = header?.value;
140
+ return typeof value === "string" ? value : undefined;
141
+ }
142
+ function parseAddress(header) {
143
+ const addresses = parseAddressList(header);
144
+ if (addresses.length === 0) {
145
+ return null;
146
+ }
147
+ const firstAddress = addresses[0];
148
+ return firstAddress || null;
149
+ }
150
+ function inferFallbackAddress(field, messageId) {
151
+ const suffix = messageId ? messageId.replace(/[^\w]/g, "").slice(-8) || "unknown" : "unknown";
152
+ return {
153
+ email: `${field}-${suffix}@mail.local`
154
+ };
155
+ }
156
+ function parseAddressList(header) {
157
+ if (!header)
158
+ return [];
159
+ return header.split(",").map((part) => part.trim()).filter(Boolean).map((value) => {
160
+ const match = value.match(/^(?:"?([^"]*)"?\s)?<?([^<>]+)>?$/);
161
+ if (!match) {
162
+ return { email: value };
163
+ }
164
+ const name = match[1]?.trim();
165
+ const email = match[2]?.trim();
166
+ if (!email) {
167
+ return { email: value };
168
+ }
169
+ return name ? { email, name } : { email };
170
+ });
171
+ }
172
+ function dedupeAddresses(addresses) {
173
+ const map = new Map;
174
+ for (const address of addresses) {
175
+ if (!address)
176
+ continue;
177
+ map.set(address.email.toLowerCase(), address);
178
+ }
179
+ return Array.from(map.values());
180
+ }
181
+ function extractContent(payload) {
182
+ if (!payload) {
183
+ return { attachments: [] };
184
+ }
185
+ const attachments = [];
186
+ const visit = (part) => {
187
+ if (!part)
188
+ return {};
189
+ if (part.filename && part.body?.attachmentId) {
190
+ attachments.push({
191
+ id: part.body.attachmentId,
192
+ filename: part.filename,
193
+ contentType: part.mimeType ?? "application/octet-stream",
194
+ sizeBytes: part.body.size ?? undefined
195
+ });
196
+ }
197
+ const mimeType = part.mimeType ?? "";
198
+ const data = part.body?.data;
199
+ if (mimeType === "text/plain" && data) {
200
+ return { text: decodeBase64Url(data) };
201
+ }
202
+ if (mimeType === "text/html" && data) {
203
+ return { html: decodeBase64Url(data) };
204
+ }
205
+ if (part.parts?.length) {
206
+ return part.parts.reduce((acc, nested) => {
207
+ const value = visit(nested);
208
+ return {
209
+ text: value.text ?? acc.text,
210
+ html: value.html ?? acc.html
211
+ };
212
+ }, {});
213
+ }
214
+ return {};
215
+ };
216
+ const { text, html } = visit(payload);
217
+ return { text, html, attachments };
218
+ }
219
+ function decodeBase64Url(data) {
220
+ const normalized = data.replace(/-/g, "+").replace(/_/g, "/");
221
+ const padding = normalized.length % 4;
222
+ const padded = padding === 0 ? normalized : normalized + "=".repeat(4 - padding);
223
+ return Buffer.from(padded, "base64").toString("utf-8");
224
+ }
225
+ export {
226
+ GmailInboundProvider
227
+ };
@@ -0,0 +1,139 @@
1
+ // src/impls/gmail-outbound.ts
2
+ import { google } from "googleapis";
3
+
4
+ class GmailOutboundProvider {
5
+ gmail;
6
+ userId;
7
+ auth;
8
+ constructor(options) {
9
+ this.auth = options.auth;
10
+ this.gmail = options.gmail ?? google.gmail({
11
+ version: "v1",
12
+ auth: options.auth
13
+ });
14
+ this.userId = options.userId ?? "me";
15
+ }
16
+ async sendEmail(message) {
17
+ const raw = encodeMessage(message);
18
+ const response = await this.gmail.users.messages.send({
19
+ userId: this.userId,
20
+ requestBody: {
21
+ raw
22
+ },
23
+ auth: this.auth
24
+ });
25
+ const id = response.data.id ?? "";
26
+ return {
27
+ id,
28
+ providerMessageId: response.data.id ?? undefined,
29
+ queuedAt: new Date
30
+ };
31
+ }
32
+ }
33
+ function encodeMessage(message) {
34
+ const headers = [
35
+ `From: ${formatAddress(message.from)}`,
36
+ `To: ${message.to.map(formatAddress).join(", ")}`,
37
+ `Subject: ${message.subject}`,
38
+ "MIME-Version: 1.0"
39
+ ];
40
+ if (message.cc?.length) {
41
+ headers.push(`Cc: ${message.cc.map(formatAddress).join(", ")}`);
42
+ }
43
+ if (message.replyTo) {
44
+ headers.push(`Reply-To: ${formatAddress(message.replyTo)}`);
45
+ }
46
+ Object.entries(message.headers ?? {}).forEach(([key, value]) => {
47
+ headers.push(`${key}: ${value}`);
48
+ });
49
+ const attachments = message.attachments ?? [];
50
+ const hasHtml = Boolean(message.htmlBody);
51
+ const hasText = Boolean(message.textBody);
52
+ const boundaryMain = `mixed_${Date.now()}`;
53
+ const boundaryAlt = `alt_${Date.now()}`;
54
+ let body = "";
55
+ if (attachments.length > 0) {
56
+ headers.push(`Content-Type: multipart/mixed; boundary="${boundaryMain}"`);
57
+ body += `\r
58
+ --${boundaryMain}\r
59
+ `;
60
+ body += buildAlternativePart(hasText, hasHtml, boundaryAlt, message);
61
+ attachments.forEach((attachment) => {
62
+ body += buildAttachmentPart(boundaryMain, attachment);
63
+ });
64
+ body += `\r
65
+ --${boundaryMain}--`;
66
+ } else if (hasText && hasHtml) {
67
+ headers.push(`Content-Type: multipart/alternative; boundary="${boundaryAlt}"`);
68
+ body += `\r
69
+ --${boundaryAlt}\r
70
+ `;
71
+ body += buildTextPart('text/plain; charset="utf-8"', message.textBody || "");
72
+ body += `\r
73
+ --${boundaryAlt}\r
74
+ `;
75
+ body += buildTextPart('text/html; charset="utf-8"', message.htmlBody || "");
76
+ body += `\r
77
+ --${boundaryAlt}--`;
78
+ } else if (hasHtml) {
79
+ headers.push('Content-Type: text/html; charset="utf-8"');
80
+ body += `\r
81
+ \r
82
+ ${message.htmlBody}`;
83
+ } else {
84
+ headers.push('Content-Type: text/plain; charset="utf-8"');
85
+ body += `\r
86
+ \r
87
+ ${message.textBody ?? ""}`;
88
+ }
89
+ const mime = `${headers.join(`\r
90
+ `)}${body}`;
91
+ return Buffer.from(mime).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
92
+ }
93
+ function buildAlternativePart(hasText, hasHtml, boundary, message) {
94
+ let content = "";
95
+ content += `Content-Type: multipart/alternative; boundary="${boundary}"\r
96
+ `;
97
+ content += `\r
98
+ `;
99
+ if (hasText) {
100
+ content += `--${boundary}\r
101
+ `;
102
+ content += buildTextPart('text/plain; charset="utf-8"', message.textBody || "");
103
+ }
104
+ if (hasHtml) {
105
+ content += `\r
106
+ --${boundary}\r
107
+ `;
108
+ content += buildTextPart('text/html; charset="utf-8"', message.htmlBody || "");
109
+ }
110
+ content += `\r
111
+ --${boundary}--`;
112
+ return content;
113
+ }
114
+ function buildTextPart(contentType, content) {
115
+ return `Content-Type: ${contentType}\r
116
+ ` + `Content-Transfer-Encoding: 7bit\r
117
+ \r
118
+ ` + content;
119
+ }
120
+ function buildAttachmentPart(boundary, attachment) {
121
+ const data = attachment.data ?? new Uint8Array;
122
+ const encoded = data.byteLength > 0 ? Buffer.from(data).toString("base64") : "";
123
+ return `\r
124
+ --${boundary}\r
125
+ ` + `Content-Type: ${attachment.contentType}; name="${attachment.filename}"\r
126
+ ` + `Content-Transfer-Encoding: base64\r
127
+ ` + `Content-Disposition: attachment; filename="${attachment.filename}"\r
128
+ \r
129
+ ` + encoded;
130
+ }
131
+ function formatAddress(address) {
132
+ if (address.name) {
133
+ return `"${address.name}" <${address.email}>`;
134
+ }
135
+ return address.email;
136
+ }
137
+ export {
138
+ GmailOutboundProvider
139
+ };
@@ -0,0 +1,191 @@
1
+ // src/impls/google-calendar.ts
2
+ import { google } from "googleapis";
3
+
4
+ class GoogleCalendarProvider {
5
+ calendar;
6
+ defaultCalendarId;
7
+ auth;
8
+ constructor(options) {
9
+ this.auth = options.auth;
10
+ this.calendar = options.calendar ?? google.calendar({
11
+ version: "v3",
12
+ auth: options.auth
13
+ });
14
+ this.defaultCalendarId = options.calendarId ?? "primary";
15
+ }
16
+ async listEvents(query) {
17
+ const response = await this.calendar.events.list({
18
+ calendarId: query.calendarId ?? this.defaultCalendarId,
19
+ timeMin: query.timeMin?.toISOString(),
20
+ timeMax: query.timeMax?.toISOString(),
21
+ maxResults: query.maxResults,
22
+ pageToken: query.pageToken,
23
+ singleEvents: true,
24
+ orderBy: "startTime",
25
+ auth: this.auth
26
+ });
27
+ const events = response.data.items?.map((item) => this.fromGoogleEvent(query.calendarId ?? this.defaultCalendarId, item)) ?? [];
28
+ return {
29
+ events,
30
+ nextPageToken: response.data.nextPageToken ?? undefined
31
+ };
32
+ }
33
+ async createEvent(input) {
34
+ const calendarId = input.calendarId ?? this.defaultCalendarId;
35
+ const response = await this.calendar.events.insert({
36
+ calendarId,
37
+ requestBody: this.toGoogleEvent(input),
38
+ conferenceDataVersion: input.conference?.create ? 1 : undefined,
39
+ auth: this.auth
40
+ });
41
+ return this.fromGoogleEvent(calendarId, response.data);
42
+ }
43
+ async updateEvent(calendarId, eventId, input) {
44
+ const response = await this.calendar.events.patch({
45
+ calendarId: calendarId ?? this.defaultCalendarId,
46
+ eventId,
47
+ requestBody: this.toGoogleEvent(input),
48
+ conferenceDataVersion: input.conference?.create ? 1 : undefined,
49
+ auth: this.auth
50
+ });
51
+ return this.fromGoogleEvent(calendarId, response.data);
52
+ }
53
+ async deleteEvent(calendarId, eventId) {
54
+ await this.calendar.events.delete({
55
+ calendarId: calendarId ?? this.defaultCalendarId,
56
+ eventId,
57
+ auth: this.auth
58
+ });
59
+ }
60
+ fromGoogleEvent(calendarId, event) {
61
+ const start = parseDateTime(event.start);
62
+ const end = parseDateTime(event.end);
63
+ const attendees = event.attendees?.map((attendee) => ({
64
+ email: attendee.email ?? "",
65
+ name: attendee.displayName ?? undefined,
66
+ optional: attendee.optional ?? undefined,
67
+ responseStatus: normalizeResponseStatus(attendee.responseStatus)
68
+ })) ?? [];
69
+ const reminders = event.reminders?.overrides?.map((reminder) => ({
70
+ method: reminder.method ?? "popup",
71
+ minutesBeforeStart: reminder.minutes ?? 0
72
+ })) ?? [];
73
+ const metadata = buildMetadata(event);
74
+ return {
75
+ id: event.id ?? "",
76
+ calendarId,
77
+ title: event.summary ?? "",
78
+ description: event.description ?? undefined,
79
+ location: event.location ?? undefined,
80
+ start,
81
+ end,
82
+ allDay: event.start?.date ? true : undefined,
83
+ attendees,
84
+ reminders,
85
+ conferenceLink: event.hangoutLink ?? event.conferenceData?.entryPoints?.find((entry) => entry.uri)?.uri ?? undefined,
86
+ metadata,
87
+ createdAt: event.created ? new Date(event.created) : undefined,
88
+ updatedAt: event.updated ? new Date(event.updated) : undefined
89
+ };
90
+ }
91
+ toGoogleEvent(input) {
92
+ const event = {};
93
+ if ("title" in input && input.title)
94
+ event.summary = input.title;
95
+ if (input.description !== undefined)
96
+ event.description = input.description;
97
+ if (input.location !== undefined)
98
+ event.location = input.location;
99
+ if (input.start) {
100
+ event.start = formatDateTime(input.start, input.allDay);
101
+ }
102
+ if (input.end) {
103
+ event.end = formatDateTime(input.end, input.allDay);
104
+ }
105
+ if (input.attendees) {
106
+ event.attendees = input.attendees.map((attendee) => ({
107
+ email: attendee.email,
108
+ displayName: attendee.name,
109
+ optional: attendee.optional,
110
+ responseStatus: attendee.responseStatus
111
+ }));
112
+ }
113
+ if (input.reminders) {
114
+ event.reminders = {
115
+ useDefault: false,
116
+ overrides: input.reminders.map((reminder) => ({
117
+ method: reminder.method,
118
+ minutes: reminder.minutesBeforeStart
119
+ }))
120
+ };
121
+ }
122
+ if (input.conference?.create) {
123
+ event.conferenceData = {
124
+ createRequest: {
125
+ requestId: `conf-${Date.now()}`
126
+ }
127
+ };
128
+ }
129
+ if (input.metadata) {
130
+ event.extendedProperties = {
131
+ ...event.extendedProperties ?? {},
132
+ private: {
133
+ ...event.extendedProperties?.private ?? {},
134
+ ...input.metadata
135
+ }
136
+ };
137
+ }
138
+ return event;
139
+ }
140
+ }
141
+ function parseDateTime(time) {
142
+ if (!time)
143
+ return new Date;
144
+ if (time.dateTime)
145
+ return new Date(time.dateTime);
146
+ if (time.date)
147
+ return new Date(`${time.date}T00:00:00`);
148
+ return new Date;
149
+ }
150
+ function formatDateTime(date, allDay) {
151
+ if (allDay) {
152
+ return { date: date.toISOString().slice(0, 10) };
153
+ }
154
+ return { dateTime: date.toISOString() };
155
+ }
156
+ function normalizeResponseStatus(status) {
157
+ if (!status)
158
+ return;
159
+ const allowed = [
160
+ "needsAction",
161
+ "declined",
162
+ "tentative",
163
+ "accepted"
164
+ ];
165
+ return allowed.includes(status) ? status : undefined;
166
+ }
167
+ function buildMetadata(event) {
168
+ const metadata = {};
169
+ if (event.status)
170
+ metadata.status = event.status;
171
+ if (event.htmlLink)
172
+ metadata.htmlLink = event.htmlLink;
173
+ if (event.iCalUID)
174
+ metadata.iCalUID = event.iCalUID;
175
+ if (event.etag)
176
+ metadata.etag = event.etag;
177
+ if (event.conferenceData?.conferenceSolution?.name) {
178
+ metadata.conferenceSolution = event.conferenceData.conferenceSolution.name;
179
+ }
180
+ if (event.extendedProperties?.private) {
181
+ Object.entries(event.extendedProperties.private).forEach(([key, value]) => {
182
+ if (typeof value === "string") {
183
+ metadata[`extended.${key}`] = value;
184
+ }
185
+ });
186
+ }
187
+ return Object.keys(metadata).length > 0 ? metadata : undefined;
188
+ }
189
+ export {
190
+ GoogleCalendarProvider
191
+ };
@@ -0,0 +1,90 @@
1
+ // src/impls/gradium-voice.ts
2
+ import { Gradium } from "@confiture-ai/gradium-sdk-js";
3
+ var FORMAT_MAP = {
4
+ mp3: "wav",
5
+ wav: "wav",
6
+ ogg: "opus",
7
+ pcm: "pcm"
8
+ };
9
+
10
+ class GradiumVoiceProvider {
11
+ client;
12
+ defaultVoiceId;
13
+ defaultOutputFormat;
14
+ constructor(options) {
15
+ this.client = options.client ?? new Gradium({
16
+ apiKey: options.apiKey,
17
+ region: options.region,
18
+ baseURL: options.baseUrl,
19
+ timeout: options.timeoutMs
20
+ });
21
+ this.defaultVoiceId = options.defaultVoiceId;
22
+ this.defaultOutputFormat = options.outputFormat;
23
+ }
24
+ async listVoices() {
25
+ const voices = await this.client.voices.list({ include_catalog: true });
26
+ return voices.map((voice) => this.fromGradiumVoice(voice));
27
+ }
28
+ async synthesize(input) {
29
+ const voiceId = input.voiceId ?? this.defaultVoiceId;
30
+ if (!voiceId) {
31
+ throw new Error("Voice ID is required for Gradium synthesis.");
32
+ }
33
+ const outputFormat = (input.format ? FORMAT_MAP[input.format] : undefined) ?? this.defaultOutputFormat ?? "wav";
34
+ const response = await this.client.tts.create({
35
+ voice_id: voiceId,
36
+ output_format: outputFormat,
37
+ text: input.text
38
+ });
39
+ return {
40
+ audio: response.raw_data,
41
+ format: input.format ?? toContractFormat(outputFormat),
42
+ sampleRateHz: input.sampleRateHz ?? response.sample_rate ?? inferSampleRate(outputFormat),
43
+ durationSeconds: undefined,
44
+ url: undefined
45
+ };
46
+ }
47
+ fromGradiumVoice(voice) {
48
+ return {
49
+ id: voice.uid,
50
+ name: voice.name,
51
+ description: voice.description ?? undefined,
52
+ language: voice.language ?? undefined,
53
+ metadata: {
54
+ startSeconds: String(voice.start_s),
55
+ ...voice.stop_s != null ? { stopSeconds: String(voice.stop_s) } : {},
56
+ filename: voice.filename
57
+ }
58
+ };
59
+ }
60
+ }
61
+ function toContractFormat(format) {
62
+ switch (format) {
63
+ case "opus":
64
+ return "ogg";
65
+ case "wav":
66
+ return "wav";
67
+ case "pcm":
68
+ case "pcm_16000":
69
+ case "pcm_24000":
70
+ return "pcm";
71
+ default:
72
+ return format;
73
+ }
74
+ }
75
+ function inferSampleRate(format) {
76
+ switch (format) {
77
+ case "ulaw_8000":
78
+ case "alaw_8000":
79
+ return 8000;
80
+ case "pcm_16000":
81
+ return 16000;
82
+ case "pcm_24000":
83
+ return 24000;
84
+ default:
85
+ return 48000;
86
+ }
87
+ }
88
+ export {
89
+ GradiumVoiceProvider
90
+ };