patchwork-os 0.2.0-beta.6.canary.21 → 0.2.0-beta.6.canary.23
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/connectorRoutes.js +384 -0
- package/dist/connectorRoutes.js.map +1 -1
- package/dist/connectors/airtable.d.ts +108 -0
- package/dist/connectors/airtable.js +396 -0
- package/dist/connectors/airtable.js.map +1 -0
- package/dist/connectors/connectorRegistry.js +63 -0
- package/dist/connectors/connectorRegistry.js.map +1 -1
- package/dist/connectors/elasticsearch.d.ts +141 -0
- package/dist/connectors/elasticsearch.js +601 -0
- package/dist/connectors/elasticsearch.js.map +1 -0
- package/dist/connectors/figma.d.ts +130 -0
- package/dist/connectors/figma.js +387 -0
- package/dist/connectors/figma.js.map +1 -0
- package/dist/connectors/gmail.js +9 -0
- package/dist/connectors/gmail.js.map +1 -1
- package/dist/connectors/googleDrive.d.ts +12 -0
- package/dist/connectors/googleDrive.js +27 -0
- package/dist/connectors/googleDrive.js.map +1 -1
- package/dist/connectors/mongodb.d.ts +139 -0
- package/dist/connectors/mongodb.js +455 -0
- package/dist/connectors/mongodb.js.map +1 -0
- package/dist/connectors/postgres.d.ts +127 -0
- package/dist/connectors/postgres.js +512 -0
- package/dist/connectors/postgres.js.map +1 -0
- package/dist/connectors/redis.d.ts +140 -0
- package/dist/connectors/redis.js +571 -0
- package/dist/connectors/redis.js.map +1 -0
- package/dist/connectors/sendgrid.d.ts +102 -0
- package/dist/connectors/sendgrid.js +423 -0
- package/dist/connectors/sendgrid.js.map +1 -0
- package/dist/connectors/twilio.d.ts +118 -0
- package/dist/connectors/twilio.js +475 -0
- package/dist/connectors/twilio.js.map +1 -0
- package/dist/connectors/webflow.d.ts +118 -0
- package/dist/connectors/webflow.js +393 -0
- package/dist/connectors/webflow.js.map +1 -0
- package/dist/recipes/tools/gmail.js +27 -5
- package/dist/recipes/tools/gmail.js.map +1 -1
- package/dist/recipes/tools/googleDrive.js +64 -0
- package/dist/recipes/tools/googleDrive.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webflow connector — read-only access to Webflow CMS via the Webflow v2 API.
|
|
3
|
+
*
|
|
4
|
+
* Auth: Site API Token via `Authorization: Bearer <token>` + `accept-version: 2.0.0`.
|
|
5
|
+
* - Env var: WEBFLOW_API_TOKEN (+ optional WEBFLOW_SITE_ID)
|
|
6
|
+
* - Stored: getSecretJsonSync("webflow") → WebflowTokens
|
|
7
|
+
*
|
|
8
|
+
* Tools: listSites, getSite, listCollections, getCollection, listCollectionItems,
|
|
9
|
+
* getCollectionItem, listForms, listFormSubmissions
|
|
10
|
+
*
|
|
11
|
+
* Extends BaseConnector for unified auth, retry, rate-limit, error handling.
|
|
12
|
+
*/
|
|
13
|
+
import { type AuthContext, BaseConnector, type ConnectorError, type ConnectorStatus } from "./baseConnector.js";
|
|
14
|
+
export interface WebflowTokens {
|
|
15
|
+
accessToken: string;
|
|
16
|
+
siteId?: string;
|
|
17
|
+
siteName?: string;
|
|
18
|
+
connected_at: string;
|
|
19
|
+
}
|
|
20
|
+
export interface WebflowSite {
|
|
21
|
+
id: string;
|
|
22
|
+
displayName?: string;
|
|
23
|
+
shortName?: string;
|
|
24
|
+
workspaceId?: string;
|
|
25
|
+
createdOn?: string;
|
|
26
|
+
lastPublished?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface WebflowCollection {
|
|
29
|
+
id: string;
|
|
30
|
+
displayName?: string;
|
|
31
|
+
singularName?: string;
|
|
32
|
+
slug?: string;
|
|
33
|
+
createdOn?: string;
|
|
34
|
+
lastUpdated?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface WebflowCollectionItem {
|
|
37
|
+
id: string;
|
|
38
|
+
cmsLocaleId?: string;
|
|
39
|
+
lastPublished?: string | null;
|
|
40
|
+
lastUpdated?: string;
|
|
41
|
+
createdOn?: string;
|
|
42
|
+
isArchived?: boolean;
|
|
43
|
+
isDraft?: boolean;
|
|
44
|
+
fieldData?: Record<string, unknown>;
|
|
45
|
+
}
|
|
46
|
+
export interface WebflowForm {
|
|
47
|
+
id: string;
|
|
48
|
+
displayName?: string;
|
|
49
|
+
createdOn?: string;
|
|
50
|
+
lastUpdated?: string;
|
|
51
|
+
fields?: Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
export interface WebflowFormSubmission {
|
|
54
|
+
id: string;
|
|
55
|
+
displayName?: string;
|
|
56
|
+
siteId?: string;
|
|
57
|
+
formId?: string;
|
|
58
|
+
dateSubmitted?: string;
|
|
59
|
+
formResponse?: Record<string, unknown>;
|
|
60
|
+
}
|
|
61
|
+
export interface WebflowListResult<T> {
|
|
62
|
+
items: T[];
|
|
63
|
+
pagination?: {
|
|
64
|
+
limit?: number;
|
|
65
|
+
offset?: number;
|
|
66
|
+
total?: number;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export declare class WebflowConnector extends BaseConnector {
|
|
70
|
+
readonly providerName = "webflow";
|
|
71
|
+
private tokens;
|
|
72
|
+
protected getOAuthConfig(): null;
|
|
73
|
+
authenticate(): Promise<AuthContext>;
|
|
74
|
+
healthCheck(): Promise<{
|
|
75
|
+
ok: boolean;
|
|
76
|
+
error?: ConnectorError;
|
|
77
|
+
}>;
|
|
78
|
+
normalizeError(error: unknown): ConnectorError;
|
|
79
|
+
getStatus(): ConnectorStatus;
|
|
80
|
+
listSites(): Promise<WebflowListResult<WebflowSite>>;
|
|
81
|
+
getSite(siteId: string): Promise<WebflowSite>;
|
|
82
|
+
listCollections(siteId: string): Promise<WebflowListResult<WebflowCollection>>;
|
|
83
|
+
getCollection(collectionId: string): Promise<WebflowCollection>;
|
|
84
|
+
listCollectionItems(collectionId: string, params?: {
|
|
85
|
+
limit?: number;
|
|
86
|
+
offset?: number;
|
|
87
|
+
}): Promise<WebflowListResult<WebflowCollectionItem>>;
|
|
88
|
+
getCollectionItem(collectionId: string, itemId: string): Promise<WebflowCollectionItem>;
|
|
89
|
+
listForms(siteId: string): Promise<WebflowListResult<WebflowForm>>;
|
|
90
|
+
listFormSubmissions(formId: string, params?: {
|
|
91
|
+
limit?: number;
|
|
92
|
+
offset?: number;
|
|
93
|
+
}): Promise<WebflowListResult<WebflowFormSubmission>>;
|
|
94
|
+
private buildHeaders;
|
|
95
|
+
}
|
|
96
|
+
export declare function loadTokens(): WebflowTokens | null;
|
|
97
|
+
export declare function saveTokens(tokens: WebflowTokens): void;
|
|
98
|
+
export declare function clearTokens(): void;
|
|
99
|
+
export declare function getWebflowConnector(): WebflowConnector;
|
|
100
|
+
export { getWebflowConnector as webflow };
|
|
101
|
+
export interface ConnectorHandlerResult {
|
|
102
|
+
status: number;
|
|
103
|
+
body: string;
|
|
104
|
+
contentType?: string;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* POST /connections/webflow/connect { accessToken }
|
|
108
|
+
* Validates by GET /v2/sites; captures first site's id + displayName.
|
|
109
|
+
*/
|
|
110
|
+
export declare function handleWebflowConnect(body: string): Promise<ConnectorHandlerResult>;
|
|
111
|
+
/**
|
|
112
|
+
* POST /connections/webflow/test
|
|
113
|
+
*/
|
|
114
|
+
export declare function handleWebflowTest(): Promise<ConnectorHandlerResult>;
|
|
115
|
+
/**
|
|
116
|
+
* DELETE /connections/webflow
|
|
117
|
+
*/
|
|
118
|
+
export declare function handleWebflowDisconnect(): ConnectorHandlerResult;
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webflow connector — read-only access to Webflow CMS via the Webflow v2 API.
|
|
3
|
+
*
|
|
4
|
+
* Auth: Site API Token via `Authorization: Bearer <token>` + `accept-version: 2.0.0`.
|
|
5
|
+
* - Env var: WEBFLOW_API_TOKEN (+ optional WEBFLOW_SITE_ID)
|
|
6
|
+
* - Stored: getSecretJsonSync("webflow") → WebflowTokens
|
|
7
|
+
*
|
|
8
|
+
* Tools: listSites, getSite, listCollections, getCollection, listCollectionItems,
|
|
9
|
+
* getCollectionItem, listForms, listFormSubmissions
|
|
10
|
+
*
|
|
11
|
+
* Extends BaseConnector for unified auth, retry, rate-limit, error handling.
|
|
12
|
+
*/
|
|
13
|
+
import { BaseConnector, } from "./baseConnector.js";
|
|
14
|
+
import { deleteSecretJsonSync, getSecretJsonSync, storeSecretJsonSync, } from "./tokenStorage.js";
|
|
15
|
+
const BASE_URL = "https://api.webflow.com/v2";
|
|
16
|
+
const MAX_LIMIT = 100;
|
|
17
|
+
export class WebflowConnector extends BaseConnector {
|
|
18
|
+
providerName = "webflow";
|
|
19
|
+
tokens = null;
|
|
20
|
+
getOAuthConfig() {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
async authenticate() {
|
|
24
|
+
const tokens = loadTokens();
|
|
25
|
+
if (!tokens) {
|
|
26
|
+
throw new Error("Webflow not connected. Run: patchwork-os connect webflow or set WEBFLOW_API_TOKEN");
|
|
27
|
+
}
|
|
28
|
+
this.tokens = tokens;
|
|
29
|
+
return {
|
|
30
|
+
token: tokens.accessToken,
|
|
31
|
+
scopes: ["read"],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
async healthCheck() {
|
|
35
|
+
try {
|
|
36
|
+
const result = await this.apiCall(async () => {
|
|
37
|
+
const res = await fetch(`${BASE_URL}/token/authorized_by`, {
|
|
38
|
+
headers: this.buildHeaders(),
|
|
39
|
+
});
|
|
40
|
+
if (!res.ok)
|
|
41
|
+
throw res;
|
|
42
|
+
return res.json();
|
|
43
|
+
});
|
|
44
|
+
if ("error" in result)
|
|
45
|
+
return { ok: false, error: result.error };
|
|
46
|
+
return { ok: true };
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
return { ok: false, error: this.normalizeError(err) };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
normalizeError(error) {
|
|
53
|
+
if (error instanceof Response) {
|
|
54
|
+
const s = error.status;
|
|
55
|
+
if (s === 400)
|
|
56
|
+
return {
|
|
57
|
+
code: "provider_error",
|
|
58
|
+
message: `Webflow API bad request (HTTP 400)`,
|
|
59
|
+
retryable: false,
|
|
60
|
+
};
|
|
61
|
+
if (s === 401)
|
|
62
|
+
return {
|
|
63
|
+
code: "auth_expired",
|
|
64
|
+
message: "Webflow authentication expired — reconnect",
|
|
65
|
+
retryable: false,
|
|
66
|
+
suggestedAction: "patchwork-os connect webflow",
|
|
67
|
+
};
|
|
68
|
+
if (s === 403)
|
|
69
|
+
return {
|
|
70
|
+
code: "permission_denied",
|
|
71
|
+
message: "Insufficient Webflow permissions",
|
|
72
|
+
retryable: false,
|
|
73
|
+
};
|
|
74
|
+
if (s === 404)
|
|
75
|
+
return {
|
|
76
|
+
code: "not_found",
|
|
77
|
+
message: "Webflow resource not found",
|
|
78
|
+
retryable: false,
|
|
79
|
+
};
|
|
80
|
+
if (s === 429)
|
|
81
|
+
return {
|
|
82
|
+
code: "rate_limited",
|
|
83
|
+
message: "Webflow API rate limit exceeded",
|
|
84
|
+
retryable: true,
|
|
85
|
+
suggestedAction: "Wait and retry",
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
code: "provider_error",
|
|
89
|
+
message: `Webflow API error: HTTP ${s}`,
|
|
90
|
+
retryable: s >= 500,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (error instanceof Error) {
|
|
94
|
+
if (error.message.includes("ENOTFOUND") ||
|
|
95
|
+
error.message.includes("ECONNREFUSED")) {
|
|
96
|
+
return {
|
|
97
|
+
code: "network_error",
|
|
98
|
+
message: `Cannot connect to Webflow: ${error.message}`,
|
|
99
|
+
retryable: true,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
code: "provider_error",
|
|
105
|
+
message: error instanceof Error ? error.message : String(error),
|
|
106
|
+
retryable: false,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
getStatus() {
|
|
110
|
+
const tokens = loadTokens();
|
|
111
|
+
return {
|
|
112
|
+
id: "webflow",
|
|
113
|
+
status: tokens ? "connected" : "disconnected",
|
|
114
|
+
lastSync: tokens?.connected_at,
|
|
115
|
+
workspace: tokens?.siteName
|
|
116
|
+
? `Webflow site ${tokens.siteName}`
|
|
117
|
+
: tokens?.siteId
|
|
118
|
+
? `Webflow site ${tokens.siteId}`
|
|
119
|
+
: undefined,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// ── API Methods ────────────────────────────────────────────────────────────
|
|
123
|
+
async listSites() {
|
|
124
|
+
const result = await this.apiCall(async () => {
|
|
125
|
+
const res = await fetch(`${BASE_URL}/sites`, {
|
|
126
|
+
headers: this.buildHeaders(),
|
|
127
|
+
});
|
|
128
|
+
if (!res.ok)
|
|
129
|
+
throw res;
|
|
130
|
+
return res.json();
|
|
131
|
+
});
|
|
132
|
+
if ("error" in result)
|
|
133
|
+
throw new Error(result.error.message);
|
|
134
|
+
const data = result.data;
|
|
135
|
+
return { items: data.sites ?? [] };
|
|
136
|
+
}
|
|
137
|
+
async getSite(siteId) {
|
|
138
|
+
const result = await this.apiCall(async () => {
|
|
139
|
+
const res = await fetch(`${BASE_URL}/sites/${encodeURIComponent(siteId)}`, { headers: this.buildHeaders() });
|
|
140
|
+
if (!res.ok)
|
|
141
|
+
throw res;
|
|
142
|
+
return res.json();
|
|
143
|
+
});
|
|
144
|
+
if ("error" in result)
|
|
145
|
+
throw new Error(result.error.message);
|
|
146
|
+
return result.data;
|
|
147
|
+
}
|
|
148
|
+
async listCollections(siteId) {
|
|
149
|
+
const result = await this.apiCall(async () => {
|
|
150
|
+
const res = await fetch(`${BASE_URL}/sites/${encodeURIComponent(siteId)}/collections`, { headers: this.buildHeaders() });
|
|
151
|
+
if (!res.ok)
|
|
152
|
+
throw res;
|
|
153
|
+
return res.json();
|
|
154
|
+
});
|
|
155
|
+
if ("error" in result)
|
|
156
|
+
throw new Error(result.error.message);
|
|
157
|
+
const data = result.data;
|
|
158
|
+
return { items: data.collections ?? [] };
|
|
159
|
+
}
|
|
160
|
+
async getCollection(collectionId) {
|
|
161
|
+
const result = await this.apiCall(async () => {
|
|
162
|
+
const res = await fetch(`${BASE_URL}/collections/${encodeURIComponent(collectionId)}`, { headers: this.buildHeaders() });
|
|
163
|
+
if (!res.ok)
|
|
164
|
+
throw res;
|
|
165
|
+
return res.json();
|
|
166
|
+
});
|
|
167
|
+
if ("error" in result)
|
|
168
|
+
throw new Error(result.error.message);
|
|
169
|
+
return result.data;
|
|
170
|
+
}
|
|
171
|
+
async listCollectionItems(collectionId, params = {}) {
|
|
172
|
+
const limit = Math.min(params.limit ?? MAX_LIMIT, MAX_LIMIT);
|
|
173
|
+
const offset = params.offset ?? 0;
|
|
174
|
+
const result = await this.apiCall(async () => {
|
|
175
|
+
const qs = new URLSearchParams();
|
|
176
|
+
qs.set("limit", String(limit));
|
|
177
|
+
qs.set("offset", String(offset));
|
|
178
|
+
const res = await fetch(`${BASE_URL}/collections/${encodeURIComponent(collectionId)}/items?${qs}`, { headers: this.buildHeaders() });
|
|
179
|
+
if (!res.ok)
|
|
180
|
+
throw res;
|
|
181
|
+
return res.json();
|
|
182
|
+
});
|
|
183
|
+
if ("error" in result)
|
|
184
|
+
throw new Error(result.error.message);
|
|
185
|
+
const data = result.data;
|
|
186
|
+
return { items: data.items ?? [], pagination: data.pagination };
|
|
187
|
+
}
|
|
188
|
+
async getCollectionItem(collectionId, itemId) {
|
|
189
|
+
const result = await this.apiCall(async () => {
|
|
190
|
+
const res = await fetch(`${BASE_URL}/collections/${encodeURIComponent(collectionId)}/items/${encodeURIComponent(itemId)}`, { headers: this.buildHeaders() });
|
|
191
|
+
if (!res.ok)
|
|
192
|
+
throw res;
|
|
193
|
+
return res.json();
|
|
194
|
+
});
|
|
195
|
+
if ("error" in result)
|
|
196
|
+
throw new Error(result.error.message);
|
|
197
|
+
return result.data;
|
|
198
|
+
}
|
|
199
|
+
async listForms(siteId) {
|
|
200
|
+
const result = await this.apiCall(async () => {
|
|
201
|
+
const res = await fetch(`${BASE_URL}/sites/${encodeURIComponent(siteId)}/forms`, { headers: this.buildHeaders() });
|
|
202
|
+
if (!res.ok)
|
|
203
|
+
throw res;
|
|
204
|
+
return res.json();
|
|
205
|
+
});
|
|
206
|
+
if ("error" in result)
|
|
207
|
+
throw new Error(result.error.message);
|
|
208
|
+
const data = result.data;
|
|
209
|
+
return { items: data.forms ?? [] };
|
|
210
|
+
}
|
|
211
|
+
async listFormSubmissions(formId, params = {}) {
|
|
212
|
+
const limit = Math.min(params.limit ?? MAX_LIMIT, MAX_LIMIT);
|
|
213
|
+
const offset = params.offset ?? 0;
|
|
214
|
+
const result = await this.apiCall(async () => {
|
|
215
|
+
const qs = new URLSearchParams();
|
|
216
|
+
qs.set("limit", String(limit));
|
|
217
|
+
qs.set("offset", String(offset));
|
|
218
|
+
const res = await fetch(`${BASE_URL}/forms/${encodeURIComponent(formId)}/submissions?${qs}`, { headers: this.buildHeaders() });
|
|
219
|
+
if (!res.ok)
|
|
220
|
+
throw res;
|
|
221
|
+
return res.json();
|
|
222
|
+
});
|
|
223
|
+
if ("error" in result)
|
|
224
|
+
throw new Error(result.error.message);
|
|
225
|
+
const data = result.data;
|
|
226
|
+
return { items: data.formSubmissions ?? [], pagination: data.pagination };
|
|
227
|
+
}
|
|
228
|
+
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
229
|
+
buildHeaders() {
|
|
230
|
+
const token = this.tokens?.accessToken ?? "";
|
|
231
|
+
return {
|
|
232
|
+
Authorization: `Bearer ${token}`,
|
|
233
|
+
Accept: "application/json",
|
|
234
|
+
"accept-version": "2.0.0",
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// ── Token persistence ────────────────────────────────────────────────────────
|
|
239
|
+
export function loadTokens() {
|
|
240
|
+
const envToken = process.env.WEBFLOW_API_TOKEN;
|
|
241
|
+
if (envToken) {
|
|
242
|
+
return {
|
|
243
|
+
accessToken: envToken,
|
|
244
|
+
siteId: process.env.WEBFLOW_SITE_ID,
|
|
245
|
+
connected_at: new Date().toISOString(),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
return getSecretJsonSync("webflow");
|
|
249
|
+
}
|
|
250
|
+
export function saveTokens(tokens) {
|
|
251
|
+
storeSecretJsonSync("webflow", tokens);
|
|
252
|
+
}
|
|
253
|
+
export function clearTokens() {
|
|
254
|
+
try {
|
|
255
|
+
deleteSecretJsonSync("webflow");
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
// ignore
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// ── Singleton instance ───────────────────────────────────────────────────────
|
|
262
|
+
let _instance = null;
|
|
263
|
+
function resetWebflowConnector() {
|
|
264
|
+
_instance = null;
|
|
265
|
+
}
|
|
266
|
+
export function getWebflowConnector() {
|
|
267
|
+
if (!_instance) {
|
|
268
|
+
_instance = new WebflowConnector();
|
|
269
|
+
}
|
|
270
|
+
return _instance;
|
|
271
|
+
}
|
|
272
|
+
export { getWebflowConnector as webflow };
|
|
273
|
+
/**
|
|
274
|
+
* POST /connections/webflow/connect { accessToken }
|
|
275
|
+
* Validates by GET /v2/sites; captures first site's id + displayName.
|
|
276
|
+
*/
|
|
277
|
+
export async function handleWebflowConnect(body) {
|
|
278
|
+
let accessToken;
|
|
279
|
+
try {
|
|
280
|
+
const parsed = JSON.parse(body);
|
|
281
|
+
if (typeof parsed.accessToken !== "string" || !parsed.accessToken) {
|
|
282
|
+
return {
|
|
283
|
+
status: 400,
|
|
284
|
+
contentType: "application/json",
|
|
285
|
+
body: JSON.stringify({ ok: false, error: "accessToken is required" }),
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
accessToken = parsed.accessToken;
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
return {
|
|
292
|
+
status: 400,
|
|
293
|
+
contentType: "application/json",
|
|
294
|
+
body: JSON.stringify({ ok: false, error: "Invalid JSON body" }),
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
const res = await fetch(`${BASE_URL}/sites`, {
|
|
299
|
+
headers: {
|
|
300
|
+
Authorization: `Bearer ${accessToken}`,
|
|
301
|
+
Accept: "application/json",
|
|
302
|
+
"accept-version": "2.0.0",
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
if (!res.ok) {
|
|
306
|
+
return {
|
|
307
|
+
status: 401,
|
|
308
|
+
contentType: "application/json",
|
|
309
|
+
body: JSON.stringify({
|
|
310
|
+
ok: false,
|
|
311
|
+
error: `Credentials rejected by Webflow (HTTP ${res.status}) — check accessToken`,
|
|
312
|
+
}),
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
const data = (await res.json());
|
|
316
|
+
const firstSite = data.sites?.[0];
|
|
317
|
+
const siteId = firstSite?.id;
|
|
318
|
+
const siteName = firstSite?.displayName;
|
|
319
|
+
const tokens = {
|
|
320
|
+
accessToken,
|
|
321
|
+
siteId,
|
|
322
|
+
siteName,
|
|
323
|
+
connected_at: new Date().toISOString(),
|
|
324
|
+
};
|
|
325
|
+
saveTokens(tokens);
|
|
326
|
+
resetWebflowConnector();
|
|
327
|
+
return {
|
|
328
|
+
status: 200,
|
|
329
|
+
contentType: "application/json",
|
|
330
|
+
body: JSON.stringify({
|
|
331
|
+
ok: true,
|
|
332
|
+
siteId,
|
|
333
|
+
siteName,
|
|
334
|
+
connectedAt: tokens.connected_at,
|
|
335
|
+
}),
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
catch (err) {
|
|
339
|
+
return {
|
|
340
|
+
status: 500,
|
|
341
|
+
contentType: "application/json",
|
|
342
|
+
body: JSON.stringify({
|
|
343
|
+
ok: false,
|
|
344
|
+
error: err instanceof Error ? err.message : String(err),
|
|
345
|
+
}),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* POST /connections/webflow/test
|
|
351
|
+
*/
|
|
352
|
+
export async function handleWebflowTest() {
|
|
353
|
+
const tokens = loadTokens();
|
|
354
|
+
if (!tokens) {
|
|
355
|
+
return {
|
|
356
|
+
status: 400,
|
|
357
|
+
contentType: "application/json",
|
|
358
|
+
body: JSON.stringify({ ok: false, error: "Webflow not connected" }),
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
try {
|
|
362
|
+
const connector = getWebflowConnector();
|
|
363
|
+
const check = await connector.healthCheck();
|
|
364
|
+
return {
|
|
365
|
+
status: check.ok ? 200 : 401,
|
|
366
|
+
contentType: "application/json",
|
|
367
|
+
body: JSON.stringify(check.ok ? { ok: true } : { ok: false, error: check.error?.message }),
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
catch (err) {
|
|
371
|
+
return {
|
|
372
|
+
status: 500,
|
|
373
|
+
contentType: "application/json",
|
|
374
|
+
body: JSON.stringify({
|
|
375
|
+
ok: false,
|
|
376
|
+
error: err instanceof Error ? err.message : String(err),
|
|
377
|
+
}),
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* DELETE /connections/webflow
|
|
383
|
+
*/
|
|
384
|
+
export function handleWebflowDisconnect() {
|
|
385
|
+
clearTokens();
|
|
386
|
+
resetWebflowConnector();
|
|
387
|
+
return {
|
|
388
|
+
status: 200,
|
|
389
|
+
contentType: "application/json",
|
|
390
|
+
body: JSON.stringify({ ok: true }),
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
//# sourceMappingURL=webflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webflow.js","sourceRoot":"","sources":["../../src/connectors/webflow.ts"],"names":[],"mappings":"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"}
|
|
@@ -47,7 +47,13 @@ async function gmailSearch(query, max, deps) {
|
|
|
47
47
|
const ids = listJson.messages ?? [];
|
|
48
48
|
// 2. Fetch details for each message
|
|
49
49
|
const messages = await Promise.all(ids.slice(0, max).map(async (m) => {
|
|
50
|
-
|
|
50
|
+
// Gmail's metadataHeaders param must be REPEATED per header, not
|
|
51
|
+
// comma-joined. `metadataHeaders=Subject,From,Date` is silently
|
|
52
|
+
// dropped server-side — the response then carries an empty
|
|
53
|
+
// headers array and every {subject,from,date} field returns "",
|
|
54
|
+
// which made downstream tools that branch on the subject (e.g.
|
|
55
|
+
// resolveMeetingNotes) silently no-op against real emails.
|
|
56
|
+
const detailUrl = `https://www.googleapis.com/gmail/v1/users/me/messages/${m.id}?format=metadata&metadataHeaders=Subject&metadataHeaders=From&metadataHeaders=Date`;
|
|
51
57
|
const detailRes = await fetch(detailUrl, {
|
|
52
58
|
headers: { Authorization: `Bearer ${token}` },
|
|
53
59
|
});
|
|
@@ -365,7 +371,14 @@ registerTool({
|
|
|
365
371
|
const results = [];
|
|
366
372
|
for (const msg of messages) {
|
|
367
373
|
const subject = msg.subject ?? "";
|
|
368
|
-
|
|
374
|
+
// Gemini's notification email format changed at least once. Earlier
|
|
375
|
+
// emails said "Notes by Gemini" in the subject; the current format
|
|
376
|
+
// (as of 2026) is `Notes: "<meeting title>" <date>` from
|
|
377
|
+
// gemini-notes@google.com — no "by Gemini" anywhere. Match both, plus
|
|
378
|
+
// the generic Drive-share variants Drive uses when the Doc is shared
|
|
379
|
+
// separately. Also accept any subject that already references a
|
|
380
|
+
// docs.google.com URL via the snippet.
|
|
381
|
+
const isGemini = /notes by gemini|^notes:\s|document shared with you|shared a document|invited you to (?:edit|view|comment)/i.test(subject) ||
|
|
369
382
|
/notes by gemini|docs\.google\.com\/document/i.test(msg.snippet ?? "");
|
|
370
383
|
if (!isGemini) {
|
|
371
384
|
// Direct Meet summary — use snippet as content
|
|
@@ -377,16 +390,21 @@ registerTool({
|
|
|
377
390
|
});
|
|
378
391
|
continue;
|
|
379
392
|
}
|
|
380
|
-
// Gemini notes — fetch full message
|
|
393
|
+
// Gemini notes — fetch full message and look for either a Doc URL OR
|
|
394
|
+
// an inlined-body. Newer Gemini emails (2026+) inline the full notes
|
|
395
|
+
// directly in the email body and DON'T include a docs.google.com URL.
|
|
396
|
+
// Older Gemini / Meet emails just link to a Drive doc.
|
|
381
397
|
let driveUrl = "";
|
|
398
|
+
let emailBody = "";
|
|
382
399
|
try {
|
|
383
400
|
const fullMsg = await gmailGetMessage(msg.id, deps);
|
|
384
401
|
const parsed = JSON.parse(fullMsg);
|
|
402
|
+
emailBody = parsed.body ?? "";
|
|
385
403
|
const links = parsed.links ?? [];
|
|
386
404
|
driveUrl = links.find(isDocsGoogleHost) ?? "";
|
|
387
405
|
if (!driveUrl) {
|
|
388
406
|
// Try extracting from body text
|
|
389
|
-
const bodyMatch = /https?:\/\/docs\.google\.com\/[^\s"'<>]+/.exec(
|
|
407
|
+
const bodyMatch = /https?:\/\/docs\.google\.com\/[^\s"'<>]+/.exec(emailBody);
|
|
390
408
|
driveUrl = bodyMatch?.[0] ?? "";
|
|
391
409
|
}
|
|
392
410
|
}
|
|
@@ -400,11 +418,15 @@ registerTool({
|
|
|
400
418
|
continue;
|
|
401
419
|
}
|
|
402
420
|
if (!driveUrl) {
|
|
421
|
+
// No linked Doc — use the email body itself as the meeting content.
|
|
422
|
+
// Gemini's inlined-notes format is what the parser already expects:
|
|
423
|
+
// a "Summary" section, topical sections, etc. Fall back to snippet
|
|
424
|
+
// only when even the body is empty (rare).
|
|
403
425
|
results.push({
|
|
404
426
|
emailId: msg.id,
|
|
405
427
|
subject,
|
|
406
428
|
source: "email",
|
|
407
|
-
content: msg.snippet ?? "",
|
|
429
|
+
content: emailBody.length > 0 ? emailBody : (msg.snippet ?? ""),
|
|
408
430
|
});
|
|
409
431
|
continue;
|
|
410
432
|
}
|