@squadbase/vite-server 0.1.8-dev.d378524 → 0.1.9-dev.08f5c5f
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/cli/index.js +78505 -64782
- package/dist/cli/{sshcrypto-VVJOJ3LR.node → sshcrypto-P3UBA7BP.node} +0 -0
- package/dist/connectors/airtable-oauth.js +3 -0
- package/dist/connectors/airtable.js +3 -0
- package/dist/connectors/amplitude.js +3 -0
- package/dist/connectors/anthropic.js +3 -0
- package/dist/connectors/asana.js +18 -2
- package/dist/connectors/attio.js +3 -0
- package/dist/connectors/aws-billing.d.ts +5 -0
- package/dist/connectors/aws-billing.js +29843 -0
- package/dist/connectors/azure-sql.d.ts +5 -0
- package/dist/connectors/azure-sql.js +657 -0
- package/dist/connectors/backlog-api-key.js +3 -0
- package/dist/connectors/clickup.d.ts +5 -0
- package/dist/connectors/clickup.js +850 -0
- package/dist/connectors/customerio.js +3 -0
- package/dist/connectors/dbt.js +3 -0
- package/dist/connectors/freshdesk.d.ts +5 -0
- package/dist/connectors/freshdesk.js +842 -0
- package/dist/connectors/freshsales.d.ts +5 -0
- package/dist/connectors/freshsales.js +867 -0
- package/dist/connectors/freshservice.d.ts +5 -0
- package/dist/connectors/freshservice.js +813 -0
- package/dist/connectors/gamma.js +3 -0
- package/dist/connectors/gemini.js +3 -0
- package/dist/connectors/github.d.ts +5 -0
- package/dist/connectors/github.js +963 -0
- package/dist/connectors/gmail-oauth.js +18 -2
- package/dist/connectors/gmail.js +236 -352
- package/dist/connectors/google-ads.js +3 -0
- package/dist/connectors/google-analytics-oauth.js +3 -0
- package/dist/connectors/google-analytics.js +3 -0
- package/dist/connectors/google-audit-log.d.ts +5 -0
- package/dist/connectors/google-audit-log.js +813 -0
- package/dist/connectors/google-calendar-oauth.js +21 -2
- package/dist/connectors/google-calendar.js +548 -453
- package/dist/connectors/google-docs.js +21 -2
- package/dist/connectors/google-drive.js +18 -2
- package/dist/connectors/google-search-console-oauth.d.ts +5 -0
- package/dist/connectors/google-search-console-oauth.js +923 -0
- package/dist/connectors/google-sheets.js +21 -2
- package/dist/connectors/google-slides.js +21 -2
- package/dist/connectors/grafana.js +3 -0
- package/dist/connectors/hubspot-oauth.js +3 -0
- package/dist/connectors/hubspot.js +3 -0
- package/dist/connectors/influxdb.js +3 -0
- package/dist/connectors/intercom-oauth.js +3 -0
- package/dist/connectors/intercom.js +3 -0
- package/dist/connectors/jdbc.d.ts +5 -0
- package/dist/connectors/jdbc.js +21097 -0
- package/dist/connectors/jira-api-key.js +3 -0
- package/dist/connectors/kintone-api-token.js +3 -0
- package/dist/connectors/kintone.js +3 -0
- package/dist/connectors/linear.js +3 -0
- package/dist/connectors/linkedin-ads.js +3 -0
- package/dist/connectors/mailchimp-oauth.js +3 -0
- package/dist/connectors/mailchimp.js +3 -0
- package/dist/connectors/meta-ads-oauth.js +3 -0
- package/dist/connectors/meta-ads.js +3 -0
- package/dist/connectors/mixpanel.js +3 -0
- package/dist/connectors/monday.d.ts +5 -0
- package/dist/connectors/monday.js +813 -0
- package/dist/connectors/notion-oauth.js +3 -0
- package/dist/connectors/notion.js +3 -0
- package/dist/connectors/openai.js +3 -0
- package/dist/connectors/oracle.d.ts +5 -0
- package/dist/connectors/oracle.js +665 -0
- package/dist/connectors/salesforce.js +3 -0
- package/dist/connectors/semrush.d.ts +5 -0
- package/dist/connectors/semrush.js +812 -0
- package/dist/connectors/sentry.js +3 -0
- package/dist/connectors/shopify-oauth.js +3 -0
- package/dist/connectors/shopify.js +3 -0
- package/dist/connectors/sqlserver.d.ts +5 -0
- package/dist/connectors/sqlserver.js +656 -0
- package/dist/connectors/stripe-api-key.js +3 -0
- package/dist/connectors/stripe-oauth.js +3 -0
- package/dist/connectors/supabase.d.ts +5 -0
- package/dist/connectors/supabase.js +582 -0
- package/dist/connectors/tiktok-ads.js +18 -2
- package/dist/connectors/wix-store.js +3 -0
- package/dist/connectors/zendesk-oauth.js +3 -0
- package/dist/connectors/zendesk.js +3 -0
- package/dist/index.js +71474 -57751
- package/dist/main.js +78053 -64330
- package/dist/{sshcrypto-VVJOJ3LR.node → sshcrypto-P3UBA7BP.node} +0 -0
- package/dist/vite-plugin.js +77960 -64237
- package/package.json +64 -2
package/dist/connectors/gmail.js
CHANGED
|
@@ -42,71 +42,28 @@ var ParameterDefinition = class {
|
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
// ../connectors/src/connectors/gmail/sdk/index.ts
|
|
46
|
-
import * as crypto from "crypto";
|
|
47
|
-
|
|
48
45
|
// ../connectors/src/connectors/gmail/parameters.ts
|
|
49
46
|
var parameters = {
|
|
50
47
|
serviceAccountKeyJsonBase64: new ParameterDefinition({
|
|
51
48
|
slug: "service-account-key-json-base64",
|
|
52
49
|
name: "Google Cloud Service Account JSON",
|
|
53
|
-
description: "The service account JSON key
|
|
50
|
+
description: "The service account JSON key. Domain-wide Delegation must be authorized in the Google Workspace admin console for the Gmail API scope. The user to impersonate is supplied per call as the `subject` argument.",
|
|
54
51
|
envVarBaseKey: "GMAIL_SERVICE_ACCOUNT_JSON_BASE64",
|
|
55
52
|
type: "base64EncodedJson",
|
|
56
53
|
secret: true,
|
|
57
54
|
required: true
|
|
58
55
|
})
|
|
59
56
|
};
|
|
60
|
-
var delegatedUserEmailParameter = new ParameterDefinition({
|
|
61
|
-
slug: "delegated-user-email",
|
|
62
|
-
name: "Delegated User Email",
|
|
63
|
-
description: "The email address of the Google Workspace user whose Gmail mailbox the service account will access via domain-wide delegation. Collected during the setup flow.",
|
|
64
|
-
envVarBaseKey: "GMAIL_DELEGATED_USER_EMAIL",
|
|
65
|
-
type: "text",
|
|
66
|
-
secret: false,
|
|
67
|
-
required: false
|
|
68
|
-
});
|
|
69
57
|
|
|
70
58
|
// ../connectors/src/connectors/gmail/sdk/index.ts
|
|
71
|
-
var TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
72
59
|
var BASE_URL = "https://gmail.googleapis.com/gmail/v1/users";
|
|
73
|
-
var SCOPE = "https://www.googleapis.com/auth/gmail.readonly";
|
|
74
|
-
function base64url(input) {
|
|
75
|
-
const buf = typeof input === "string" ? Buffer.from(input) : input;
|
|
76
|
-
return buf.toString("base64url");
|
|
77
|
-
}
|
|
78
|
-
function buildJwt(clientEmail, privateKey, subject, nowSec) {
|
|
79
|
-
const header = base64url(JSON.stringify({ alg: "RS256", typ: "JWT" }));
|
|
80
|
-
const payload = base64url(
|
|
81
|
-
JSON.stringify({
|
|
82
|
-
iss: clientEmail,
|
|
83
|
-
sub: subject,
|
|
84
|
-
scope: SCOPE,
|
|
85
|
-
aud: TOKEN_URL,
|
|
86
|
-
iat: nowSec,
|
|
87
|
-
exp: nowSec + 3600
|
|
88
|
-
})
|
|
89
|
-
);
|
|
90
|
-
const signingInput = `${header}.${payload}`;
|
|
91
|
-
const sign = crypto.createSign("RSA-SHA256");
|
|
92
|
-
sign.update(signingInput);
|
|
93
|
-
sign.end();
|
|
94
|
-
const signature = base64url(sign.sign(privateKey));
|
|
95
|
-
return `${signingInput}.${signature}`;
|
|
96
|
-
}
|
|
97
60
|
function createClient(params) {
|
|
98
61
|
const serviceAccountKeyJsonBase64 = params[parameters.serviceAccountKeyJsonBase64.slug];
|
|
99
|
-
const delegatedUserEmail = params[delegatedUserEmailParameter.slug];
|
|
100
62
|
if (!serviceAccountKeyJsonBase64) {
|
|
101
63
|
throw new Error(
|
|
102
64
|
`gmail: missing required parameter: ${parameters.serviceAccountKeyJsonBase64.slug}`
|
|
103
65
|
);
|
|
104
66
|
}
|
|
105
|
-
if (!delegatedUserEmail) {
|
|
106
|
-
throw new Error(
|
|
107
|
-
`gmail: missing required parameter: ${delegatedUserEmailParameter.slug}`
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
67
|
let serviceAccountKey;
|
|
111
68
|
try {
|
|
112
69
|
const decoded = Buffer.from(
|
|
@@ -124,143 +81,28 @@ function createClient(params) {
|
|
|
124
81
|
"gmail: service account key JSON must contain client_email and private_key"
|
|
125
82
|
);
|
|
126
83
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
body: new URLSearchParams({
|
|
144
|
-
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
|
145
|
-
assertion: jwt
|
|
146
|
-
})
|
|
147
|
-
});
|
|
148
|
-
if (!response.ok) {
|
|
149
|
-
const text = await response.text();
|
|
150
|
-
throw new Error(
|
|
151
|
-
`gmail: token exchange failed (${response.status}): ${text}`
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
const data = await response.json();
|
|
155
|
-
cachedToken = data.access_token;
|
|
156
|
-
tokenExpiresAt = nowSec + data.expires_in;
|
|
157
|
-
return cachedToken;
|
|
158
|
-
}
|
|
159
|
-
async function request(path2, init) {
|
|
160
|
-
const accessToken = await getAccessToken();
|
|
161
|
-
const url = `${BASE_URL}${path2.startsWith("/") ? "" : "/"}${path2}`;
|
|
162
|
-
const headers = new Headers(init?.headers);
|
|
163
|
-
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
164
|
-
return fetch(url, { ...init, headers });
|
|
165
|
-
}
|
|
166
|
-
async function getProfile() {
|
|
167
|
-
const response = await request("/me/profile");
|
|
168
|
-
if (!response.ok) {
|
|
169
|
-
const body = await response.text();
|
|
170
|
-
throw new Error(
|
|
171
|
-
`gmail: getProfile failed (${response.status}): ${body}`
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
return await response.json();
|
|
175
|
-
}
|
|
176
|
-
async function listLabels() {
|
|
177
|
-
const response = await request("/me/labels");
|
|
178
|
-
if (!response.ok) {
|
|
179
|
-
const body = await response.text();
|
|
180
|
-
throw new Error(
|
|
181
|
-
`gmail: listLabels failed (${response.status}): ${body}`
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
return await response.json();
|
|
185
|
-
}
|
|
186
|
-
async function listMessages(options) {
|
|
187
|
-
const params2 = new URLSearchParams();
|
|
188
|
-
if (options?.q) params2.set("q", options.q);
|
|
189
|
-
if (options?.maxResults) params2.set("maxResults", String(options.maxResults));
|
|
190
|
-
if (options?.pageToken) params2.set("pageToken", options.pageToken);
|
|
191
|
-
if (options?.labelIds) {
|
|
192
|
-
for (const labelId of options.labelIds) {
|
|
193
|
-
params2.append("labelIds", labelId);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
const qs = params2.toString();
|
|
197
|
-
const response = await request(`/me/messages${qs ? `?${qs}` : ""}`);
|
|
198
|
-
if (!response.ok) {
|
|
199
|
-
const body = await response.text();
|
|
200
|
-
throw new Error(
|
|
201
|
-
`gmail: listMessages failed (${response.status}): ${body}`
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
return await response.json();
|
|
205
|
-
}
|
|
206
|
-
async function getMessage(messageId, format) {
|
|
207
|
-
const p = new URLSearchParams();
|
|
208
|
-
if (format) p.set("format", format);
|
|
209
|
-
const qs = p.toString();
|
|
210
|
-
const response = await request(
|
|
211
|
-
`/me/messages/${encodeURIComponent(messageId)}${qs ? `?${qs}` : ""}`
|
|
212
|
-
);
|
|
213
|
-
if (!response.ok) {
|
|
214
|
-
const body = await response.text();
|
|
215
|
-
throw new Error(
|
|
216
|
-
`gmail: getMessage failed (${response.status}): ${body}`
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
return await response.json();
|
|
220
|
-
}
|
|
221
|
-
async function listThreads(options) {
|
|
222
|
-
const params2 = new URLSearchParams();
|
|
223
|
-
if (options?.q) params2.set("q", options.q);
|
|
224
|
-
if (options?.maxResults) params2.set("maxResults", String(options.maxResults));
|
|
225
|
-
if (options?.pageToken) params2.set("pageToken", options.pageToken);
|
|
226
|
-
if (options?.labelIds) {
|
|
227
|
-
for (const labelId of options.labelIds) {
|
|
228
|
-
params2.append("labelIds", labelId);
|
|
84
|
+
return {
|
|
85
|
+
async requestWithDelegation(path2, { subject, scopes, init }) {
|
|
86
|
+
const { GoogleAuth } = await import("google-auth-library");
|
|
87
|
+
const auth = new GoogleAuth({
|
|
88
|
+
credentials: {
|
|
89
|
+
client_email: serviceAccountKey.client_email,
|
|
90
|
+
private_key: serviceAccountKey.private_key
|
|
91
|
+
},
|
|
92
|
+
scopes,
|
|
93
|
+
clientOptions: { subject }
|
|
94
|
+
});
|
|
95
|
+
const token = await auth.getAccessToken();
|
|
96
|
+
if (!token) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`gmail: failed to obtain access token for ${subject}`
|
|
99
|
+
);
|
|
229
100
|
}
|
|
101
|
+
const url = `${BASE_URL}${path2.startsWith("/") ? "" : "/"}${path2}`;
|
|
102
|
+
const headers = new Headers(init?.headers);
|
|
103
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
104
|
+
return fetch(url, { ...init, headers });
|
|
230
105
|
}
|
|
231
|
-
const qs = params2.toString();
|
|
232
|
-
const response = await request(`/me/threads${qs ? `?${qs}` : ""}`);
|
|
233
|
-
if (!response.ok) {
|
|
234
|
-
const body = await response.text();
|
|
235
|
-
throw new Error(
|
|
236
|
-
`gmail: listThreads failed (${response.status}): ${body}`
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
return await response.json();
|
|
240
|
-
}
|
|
241
|
-
async function getThread(threadId, format) {
|
|
242
|
-
const p = new URLSearchParams();
|
|
243
|
-
if (format) p.set("format", format);
|
|
244
|
-
const qs = p.toString();
|
|
245
|
-
const response = await request(
|
|
246
|
-
`/me/threads/${encodeURIComponent(threadId)}${qs ? `?${qs}` : ""}`
|
|
247
|
-
);
|
|
248
|
-
if (!response.ok) {
|
|
249
|
-
const body = await response.text();
|
|
250
|
-
throw new Error(
|
|
251
|
-
`gmail: getThread failed (${response.status}): ${body}`
|
|
252
|
-
);
|
|
253
|
-
}
|
|
254
|
-
return await response.json();
|
|
255
|
-
}
|
|
256
|
-
return {
|
|
257
|
-
request,
|
|
258
|
-
getProfile,
|
|
259
|
-
listLabels,
|
|
260
|
-
listMessages,
|
|
261
|
-
getMessage,
|
|
262
|
-
listThreads,
|
|
263
|
-
getThread
|
|
264
106
|
};
|
|
265
107
|
}
|
|
266
108
|
|
|
@@ -317,6 +159,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
317
159
|
releaseFlag;
|
|
318
160
|
proxyPolicy;
|
|
319
161
|
experimentalAttributes;
|
|
162
|
+
categories;
|
|
320
163
|
onboarding;
|
|
321
164
|
systemPrompt;
|
|
322
165
|
tools;
|
|
@@ -332,6 +175,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
332
175
|
this.releaseFlag = config.releaseFlag;
|
|
333
176
|
this.proxyPolicy = config.proxyPolicy;
|
|
334
177
|
this.experimentalAttributes = config.experimentalAttributes;
|
|
178
|
+
this.categories = config.categories ?? [];
|
|
335
179
|
this.onboarding = config.onboarding;
|
|
336
180
|
this.systemPrompt = config.systemPrompt;
|
|
337
181
|
this.tools = config.tools;
|
|
@@ -411,42 +255,65 @@ var AUTH_TYPES = {
|
|
|
411
255
|
USER_PASSWORD: "user-password"
|
|
412
256
|
};
|
|
413
257
|
|
|
414
|
-
// ../connectors/src/
|
|
258
|
+
// ../connectors/src/lib/normalize-path.ts
|
|
259
|
+
function normalizeRequestPath(path2, basePathSegment) {
|
|
260
|
+
let p = path2.trim();
|
|
261
|
+
if (!p.startsWith("/")) p = "/" + p;
|
|
262
|
+
if (p === basePathSegment || p.startsWith(basePathSegment + "/")) {
|
|
263
|
+
p = p.slice(basePathSegment.length) || "/";
|
|
264
|
+
}
|
|
265
|
+
return p;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ../connectors/src/connectors/gmail/tools/request-with-delegation.ts
|
|
415
269
|
import { z } from "zod";
|
|
416
|
-
var
|
|
270
|
+
var BASE_HOST = "https://gmail.googleapis.com";
|
|
271
|
+
var BASE_PATH_SEGMENT = "/gmail/v1/users";
|
|
272
|
+
var BASE_URL2 = `${BASE_HOST}${BASE_PATH_SEGMENT}`;
|
|
417
273
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
274
|
+
function decodeServiceAccount(keyJsonBase64) {
|
|
275
|
+
const decoded = Buffer.from(keyJsonBase64, "base64").toString("utf-8");
|
|
276
|
+
return JSON.parse(decoded);
|
|
277
|
+
}
|
|
418
278
|
var inputSchema = z.object({
|
|
419
279
|
toolUseIntent: z.string().optional().describe(
|
|
420
280
|
"Brief description of what you intend to accomplish with this tool call"
|
|
421
281
|
),
|
|
422
282
|
connectionId: z.string().describe("ID of the Gmail service account connection to use"),
|
|
423
|
-
method: z.enum(["GET"]).describe("HTTP method
|
|
283
|
+
method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).describe("HTTP method"),
|
|
424
284
|
path: z.string().describe(
|
|
425
|
-
"API path appended to https://gmail.googleapis.com/gmail/v1/users (e.g., '/me/messages', '/me/messages/{id}', '/me/labels'). Use '/me' as the userId."
|
|
285
|
+
"API path appended to https://gmail.googleapis.com/gmail/v1/users (e.g., '/me/messages', '/me/messages/{id}', '/me/labels'). Use '/me' as the userId \u2014 the impersonated user."
|
|
286
|
+
),
|
|
287
|
+
subject: z.string().describe(
|
|
288
|
+
"Email of the Workspace user to impersonate via Domain-wide Delegation. The token will be issued as this user."
|
|
289
|
+
),
|
|
290
|
+
scopes: z.array(z.string()).describe(
|
|
291
|
+
"OAuth scopes the token must include. This connector currently supports read-only operations only \u2014 pass ['https://www.googleapis.com/auth/gmail.readonly']. Per-endpoint scope reference: https://developers.google.com/gmail/api/auth/scopes"
|
|
426
292
|
),
|
|
427
293
|
queryParams: z.record(z.string(), z.string()).optional().describe(
|
|
428
294
|
"Query parameters to append to the URL (e.g., { q: 'from:example@gmail.com', maxResults: '10' })"
|
|
429
|
-
)
|
|
295
|
+
),
|
|
296
|
+
body: z.record(z.string(), z.unknown()).optional().describe("JSON request body for POST/PUT/PATCH")
|
|
430
297
|
});
|
|
431
298
|
var outputSchema = z.discriminatedUnion("success", [
|
|
432
299
|
z.object({
|
|
433
300
|
success: z.literal(true),
|
|
434
301
|
status: z.number(),
|
|
435
|
-
data: z.record(z.string(), z.unknown())
|
|
302
|
+
data: z.record(z.string(), z.unknown()),
|
|
303
|
+
serviceAccountEmail: z.string()
|
|
436
304
|
}),
|
|
437
305
|
z.object({
|
|
438
306
|
success: z.literal(false),
|
|
439
|
-
error: z.string()
|
|
307
|
+
error: z.string(),
|
|
308
|
+
serviceAccountEmail: z.string().optional()
|
|
440
309
|
})
|
|
441
310
|
]);
|
|
442
|
-
var
|
|
443
|
-
name: "
|
|
444
|
-
description: `
|
|
445
|
-
Authentication is handled automatically using a service account with domain-wide delegation.
|
|
446
|
-
All paths are relative to https://gmail.googleapis.com/gmail/v1/users. Use '/me' as the userId prefix (e.g., '/me/messages').`,
|
|
311
|
+
var requestWithDelegationTool = new ConnectorTool({
|
|
312
|
+
name: "request_with_delegation",
|
|
313
|
+
description: "Call the Gmail API on behalf of the specified Workspace user via Domain-wide Delegation. Read-only operations only. Pass `subject` as the target user email and `scopes` as ['https://www.googleapis.com/auth/gmail.readonly']. Paths are relative to https://gmail.googleapis.com/gmail/v1/users \u2014 use '/me' as the userId prefix (e.g., '/me/messages'). Requires Domain-wide Delegation to be authorized for the service account in the Workspace admin console.",
|
|
447
314
|
inputSchema,
|
|
448
315
|
outputSchema,
|
|
449
|
-
async execute({ connectionId, method, path: path2, queryParams }, connections) {
|
|
316
|
+
async execute({ connectionId, method, path: path2, subject, scopes, queryParams, body }, connections) {
|
|
450
317
|
const connection2 = connections.find((c) => c.id === connectionId);
|
|
451
318
|
if (!connection2) {
|
|
452
319
|
return {
|
|
@@ -454,31 +321,41 @@ All paths are relative to https://gmail.googleapis.com/gmail/v1/users. Use '/me'
|
|
|
454
321
|
error: `Connection ${connectionId} not found`
|
|
455
322
|
};
|
|
456
323
|
}
|
|
324
|
+
const keyJsonBase64 = parameters.serviceAccountKeyJsonBase64.getValue(connection2);
|
|
325
|
+
let serviceAccount;
|
|
326
|
+
try {
|
|
327
|
+
serviceAccount = decodeServiceAccount(keyJsonBase64);
|
|
328
|
+
} catch (err) {
|
|
329
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
330
|
+
return {
|
|
331
|
+
success: false,
|
|
332
|
+
error: `Failed to decode service account key: ${msg}`
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
const serviceAccountEmail = serviceAccount.client_email;
|
|
457
336
|
console.log(
|
|
458
|
-
`[connector-request] gmail/${connection2.name}: ${method} ${path2}`
|
|
337
|
+
`[connector-request] gmail/${connection2.name}: ${method} ${path2} subject=${subject}`
|
|
459
338
|
);
|
|
460
339
|
try {
|
|
461
340
|
const { GoogleAuth } = await import("google-auth-library");
|
|
462
|
-
const keyJsonBase64 = parameters.serviceAccountKeyJsonBase64.getValue(connection2);
|
|
463
|
-
const delegatedUserEmail = delegatedUserEmailParameter.getValue(connection2);
|
|
464
|
-
const credentials = JSON.parse(
|
|
465
|
-
Buffer.from(keyJsonBase64, "base64").toString("utf-8")
|
|
466
|
-
);
|
|
467
341
|
const auth = new GoogleAuth({
|
|
468
|
-
credentials
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
342
|
+
credentials: {
|
|
343
|
+
client_email: serviceAccount.client_email,
|
|
344
|
+
private_key: serviceAccount.private_key
|
|
345
|
+
},
|
|
346
|
+
scopes,
|
|
347
|
+
clientOptions: { subject }
|
|
473
348
|
});
|
|
474
349
|
const token = await auth.getAccessToken();
|
|
475
350
|
if (!token) {
|
|
476
351
|
return {
|
|
477
352
|
success: false,
|
|
478
|
-
error: "Failed to obtain access token"
|
|
353
|
+
error: "Failed to obtain access token",
|
|
354
|
+
serviceAccountEmail
|
|
479
355
|
};
|
|
480
356
|
}
|
|
481
|
-
|
|
357
|
+
const normalizedPath = normalizeRequestPath(path2, BASE_PATH_SEGMENT);
|
|
358
|
+
let url = `${BASE_URL2}${normalizedPath}`;
|
|
482
359
|
if (queryParams) {
|
|
483
360
|
const searchParams = new URLSearchParams(queryParams);
|
|
484
361
|
url += `?${searchParams.toString()}`;
|
|
@@ -486,111 +363,143 @@ All paths are relative to https://gmail.googleapis.com/gmail/v1/users. Use '/me'
|
|
|
486
363
|
const controller = new AbortController();
|
|
487
364
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
488
365
|
try {
|
|
366
|
+
const hasBody = body != null && ["POST", "PUT", "PATCH"].includes(method);
|
|
489
367
|
const response = await fetch(url, {
|
|
490
368
|
method,
|
|
491
369
|
headers: {
|
|
492
370
|
Authorization: `Bearer ${token}`,
|
|
493
371
|
"Content-Type": "application/json"
|
|
494
372
|
},
|
|
373
|
+
body: hasBody ? JSON.stringify(body) : void 0,
|
|
495
374
|
signal: controller.signal
|
|
496
375
|
});
|
|
497
|
-
const data = await response.json();
|
|
376
|
+
const data = await response.json().catch(() => ({}));
|
|
498
377
|
if (!response.ok) {
|
|
499
|
-
const
|
|
500
|
-
|
|
378
|
+
const errorObj = data?.error;
|
|
379
|
+
const errorMessage = errorObj?.message ?? (typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`);
|
|
380
|
+
return {
|
|
381
|
+
success: false,
|
|
382
|
+
error: errorMessage,
|
|
383
|
+
serviceAccountEmail
|
|
384
|
+
};
|
|
501
385
|
}
|
|
502
|
-
return {
|
|
386
|
+
return {
|
|
387
|
+
success: true,
|
|
388
|
+
status: response.status,
|
|
389
|
+
data,
|
|
390
|
+
serviceAccountEmail
|
|
391
|
+
};
|
|
503
392
|
} finally {
|
|
504
393
|
clearTimeout(timeout);
|
|
505
394
|
}
|
|
506
395
|
} catch (err) {
|
|
507
396
|
const msg = err instanceof Error ? err.message : String(err);
|
|
508
|
-
return {
|
|
397
|
+
return {
|
|
398
|
+
success: false,
|
|
399
|
+
error: msg,
|
|
400
|
+
serviceAccountEmail
|
|
401
|
+
};
|
|
509
402
|
}
|
|
510
403
|
}
|
|
511
404
|
});
|
|
512
405
|
|
|
513
406
|
// ../connectors/src/connectors/gmail/setup.ts
|
|
514
|
-
var
|
|
407
|
+
var requestWithDelegationToolName = `gmail-service-account_${requestWithDelegationTool.name}`;
|
|
408
|
+
var READONLY_SCOPES = '["https://www.googleapis.com/auth/gmail.readonly"]';
|
|
515
409
|
var gmailOnboarding = new ConnectorOnboarding({
|
|
516
410
|
connectionSetupInstructions: {
|
|
517
|
-
ja:
|
|
411
|
+
ja: `Gmail\uFF08\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\uFF09\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3044\u307E\u3059\u3002\u30A2\u30AF\u30BB\u30B9\u5BFE\u8C61\u306E Workspace \u30E6\u30FC\u30B6\u30FC\u3092\u30E6\u30FC\u30B6\u30FC\u304B\u3089\u805E\u304D\u3001Project Knowledge \u306B\u8A18\u9332\u3057\u307E\u3059\u3002
|
|
518
412
|
|
|
519
|
-
1. \`askUserQuestion\` \u3067\
|
|
413
|
+
1. \`askUserQuestion\` \u3067\u5BFE\u8C61\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u805E\u304F:
|
|
520
414
|
- \`type\`: \`"freeText"\`
|
|
521
|
-
- \`question\`: \u300CGmail\u3092\u53C2\u7167\u3059\
|
|
522
|
-
- \`placeholder\`: \`"
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
- \`options\`: \`[{ value: <\u5165\u529B\u3055\u308C\u305F\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9>, label: <\u540C\u3058\u5024> }]\`\uFF081\u4EF6\u306E\u307F\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u306F\u81EA\u52D5\u9078\u629E\u3055\u308C\u308B\uFF09
|
|
526
|
-
3. \`${requestToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u30E6\u30FC\u30B6\u30FC\u306E\u30D7\u30ED\u30D5\u30A3\u30FC\u30EB\u3092\u53D6\u5F97\u3059\u308B:
|
|
415
|
+
- \`question\`: \u300CGmail \u3092\u53C2\u7167\u3059\u308B Google Workspace \u30E6\u30FC\u30B6\u30FC\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u53EF\u3001\u30AB\u30F3\u30DE\u533A\u5207\u308A\uFF09\u300D
|
|
416
|
+
- \`placeholder\`: \`"alice@example.com, bob@example.com"\`
|
|
417
|
+
|
|
418
|
+
2. \u30E6\u30FC\u30B6\u30FC\u304B\u3089\u53D7\u3051\u53D6\u3063\u305F\u6587\u5B57\u5217\u304B\u3089\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u62BD\u51FA\u3059\u308B\u3002\u5404 \`<email>\` \u306B\u3064\u3044\u3066 \`${requestWithDelegationToolName}\` \u3092\u4EE5\u4E0B\u306E\u5F15\u6570\u3067\u547C\u3073\u3001Domain-wide Delegation \u7D4C\u7531\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u304B\u3092\u78BA\u8A8D\u3059\u308B:
|
|
527
419
|
- \`method\`: \`"GET"\`
|
|
528
420
|
- \`path\`: \`"/me/profile"\`
|
|
529
|
-
|
|
530
|
-
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
-
|
|
535
|
-
-
|
|
421
|
+
- \`subject\`: \`<email>\`
|
|
422
|
+
- \`scopes\`: \`${READONLY_SCOPES}\`
|
|
423
|
+
|
|
424
|
+
3. \u5931\u6557\u3057\u305F\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u304C\u3042\u308C\u3070\u3001\u30A8\u30E9\u30FC\u30EC\u30B9\u30DD\u30F3\u30B9\u306E \`serviceAccountEmail\` \u30D5\u30A3\u30FC\u30EB\u30C9\u304B\u3089\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u53D6\u308A\u51FA\u3057\u3001\`askUserQuestion\` \u3067\u6B21\u306E\u9078\u629E\u80A2\u3092\u63D0\u793A\u3059\u308B\u3002\`question\` \u306B\u306F\u6B21\u306E\u6848\u5185\u6587\u3092\u542B\u3081\u308B: \u300C\u6B21\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306B\u30A2\u30AF\u30BB\u30B9\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F: {\u5931\u6557\u30A2\u30C9\u30EC\u30B9\u4E00\u89A7}\u3002\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8 \`<serviceAccountEmail>\` \u306E Domain-wide Delegation \u304C Workspace \u7BA1\u7406\u30B3\u30F3\u30BD\u30FC\u30EB\u3067\u627F\u8A8D\u3055\u308C\u3066\u3044\u308B\u304B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\uFF08[\u8A2D\u5B9A\u30AC\u30A4\u30C9](https://support.google.com/a/answer/162106)\uFF09\u300D\u3002
|
|
425
|
+
- \`options\`: \`[{ label: "\u30C9\u30E1\u30A4\u30F3\u5168\u4F53\u306E\u59D4\u4EFB\u3092\u627F\u8A8D\u3057\u305F\u306E\u3067\u30EA\u30C8\u30E9\u30A4", value: "retry" }, { label: "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u5165\u529B\u3057\u76F4\u3059", value: "restart" }]\`
|
|
426
|
+
- \u300C\u30EA\u30C8\u30E9\u30A4\u300D: \u76F4\u524D\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u30EA\u30B9\u30C8\u3067\u30B9\u30C6\u30C3\u30D7 2 \u3092\u518D\u5B9F\u884C
|
|
427
|
+
- \u300C\u5165\u529B\u3057\u76F4\u3059\u300D: \u30B9\u30C6\u30C3\u30D7 1 \u304B\u3089\u518D\u5B9F\u884C
|
|
428
|
+
|
|
429
|
+
4. \u5168\u30A2\u30C9\u30EC\u30B9\u304C\u6210\u529F\u3057\u305F\u3089 \`finalizeSetup\` \u3092\u547C\u3076\u3002\u691C\u8A3C\u6E08\u307F\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\uFF08\u30B9\u30C6\u30C3\u30D7 2 \u3067 \`subject\` \u3068\u3057\u3066\u4F7F\u3063\u305F\u5404 \`<email>\`\uFF09\u3092\u3053\u306E\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30B9\u30B3\u30FC\u30D7\u60C5\u5831\u3068\u3057\u3066\u8A18\u9332\u3059\u308B\u3002
|
|
536
430
|
|
|
537
431
|
#### \u5236\u7D04
|
|
538
|
-
-
|
|
539
|
-
- \u30C4\u30FC\u30EB\u9593\
|
|
540
|
-
en: `
|
|
432
|
+
- \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u4E2D\u306B\u30E1\u30C3\u30BB\u30FC\u30B8\u672C\u6587\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3053\u3068\u3002\u8A31\u53EF\u3055\u308C\u3066\u3044\u308B\u306E\u306F\u30B9\u30C6\u30C3\u30D7 2 \u306E \`/me/profile\` \u78BA\u8A8D\u306E\u307F
|
|
433
|
+
- \u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057\u306E\u9593\u306F 1 \u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057`,
|
|
434
|
+
en: `Set up the Gmail (Service Account) connection. Ask the user which Workspace users to access, verify each via Domain-wide Delegation, and record them in Project Knowledge.
|
|
541
435
|
|
|
542
|
-
1. Call \`askUserQuestion\` to
|
|
436
|
+
1. Call \`askUserQuestion\` to collect target emails:
|
|
543
437
|
- \`type\`: \`"freeText"\`
|
|
544
|
-
- \`question\`: "
|
|
545
|
-
- \`placeholder\`: \`"
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
- \`options\`: \`[{ value: <entered email>, label: <same value> }]\` (a single option is auto-selected)
|
|
549
|
-
3. Call \`${requestToolName}\` to get the user's profile:
|
|
438
|
+
- \`question\`: "Enter the Google Workspace user email(s) whose Gmail mailbox you want to access (comma-separated for multiple)"
|
|
439
|
+
- \`placeholder\`: \`"alice@example.com, bob@example.com"\`
|
|
440
|
+
|
|
441
|
+
2. Extract individual emails from the response. For each \`<email>\`, verify Domain-wide Delegation access by calling \`${requestWithDelegationToolName}\`:
|
|
550
442
|
- \`method\`: \`"GET"\`
|
|
551
443
|
- \`path\`: \`"/me/profile"\`
|
|
552
|
-
|
|
553
|
-
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
-
|
|
558
|
-
-
|
|
444
|
+
- \`subject\`: \`<email>\`
|
|
445
|
+
- \`scopes\`: \`${READONLY_SCOPES}\`
|
|
446
|
+
|
|
447
|
+
3. For any failed email, take \`serviceAccountEmail\` from the error response and call \`askUserQuestion\`. Include this guidance in \`question\`: "Could not access Gmail for {failed emails}. Verify Domain-wide Delegation for service account \`<serviceAccountEmail>\` in the Workspace admin console ([setup guide](https://support.google.com/a/answer/162106))".
|
|
448
|
+
- \`options\`: \`[{ label: "Authorized Domain-wide Delegation \u2014 retry", value: "retry" }, { label: "Re-enter the email addresses", value: "restart" }]\`
|
|
449
|
+
- On "retry" \u2192 re-run step 2 with the previously entered email list
|
|
450
|
+
- On "Re-enter" \u2192 re-run step 1
|
|
451
|
+
|
|
452
|
+
4. Once every email succeeds, call \`finalizeSetup\`. Record the verified email addresses (the \`<email>\` values used as \`subject\` in step 2) as this connection's scope info.
|
|
559
453
|
|
|
560
454
|
#### Constraints
|
|
561
|
-
-
|
|
562
|
-
- Write
|
|
455
|
+
- Do NOT read message bodies during setup. Only the \`/me/profile\` verification is permitted
|
|
456
|
+
- Write at most 1 sentence between tool calls`
|
|
563
457
|
},
|
|
564
458
|
dataOverviewInstructions: {
|
|
565
|
-
en: `
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
459
|
+
en: `Pick ONE target user and use that user's email as \`subject\` for every call below. Pass \`scopes: ${READONLY_SCOPES}\` every time.
|
|
460
|
+
|
|
461
|
+
1. \`method=GET\`, \`path=/me/labels\` to list labels.
|
|
462
|
+
2. \`method=GET\`, \`path=/me/messages?maxResults=5\` to fetch recent message IDs.
|
|
463
|
+
3. For each message id, \`method=GET\`, \`path=/me/messages/{id}?format=metadata\`.`,
|
|
464
|
+
ja: `\u4EE3\u7406\u5BFE\u8C61\u306E\u30E6\u30FC\u30B6\u30FC\u3092 1 \u4EBA\u9078\u3073\u3001\u4EE5\u4E0B\u306E\u30B9\u30C6\u30C3\u30D7\u3067 \`subject\` \u5F15\u6570\u3068\u3057\u3066\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044\u3002\`scopes\` \u306F\u6BCE\u56DE \`${READONLY_SCOPES}\` \u3092\u6E21\u3057\u307E\u3059\u3002
|
|
465
|
+
|
|
466
|
+
1. \`method=GET\`\u3001\`path=/me/labels\` \u3067\u30E9\u30D9\u30EB\u4E00\u89A7\u3092\u53D6\u5F97
|
|
467
|
+
2. \`method=GET\`\u3001\`path=/me/messages?maxResults=5\` \u3067\u6700\u8FD1\u306E\u30E1\u30C3\u30BB\u30FC\u30B8 ID \u3092\u53D6\u5F97
|
|
468
|
+
3. \u5404\u30E1\u30C3\u30BB\u30FC\u30B8 ID \u306B\u3064\u3044\u3066 \`method=GET\`\u3001\`path=/me/messages/{id}?format=metadata\``
|
|
571
469
|
}
|
|
572
470
|
});
|
|
573
471
|
|
|
574
472
|
// ../connectors/src/connectors/gmail/index.ts
|
|
575
|
-
var tools = {
|
|
473
|
+
var tools = { request_with_delegation: requestWithDelegationTool };
|
|
576
474
|
var gmailConnector = new ConnectorPlugin({
|
|
577
475
|
slug: "gmail",
|
|
578
476
|
authType: AUTH_TYPES.SERVICE_ACCOUNT,
|
|
579
477
|
name: "Gmail",
|
|
580
|
-
description: "Connect to Gmail for email data access using a service account with domain-wide delegation.
|
|
478
|
+
description: "Connect to Gmail for email data access using a service account with domain-wide delegation. Currently read-only (messages, threads, labels).",
|
|
581
479
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/4V3rfaSc1ksFIt2eHBNIwJ/7f3be41a154a6d96dcf229ed0e5858c9/Gmail_icon__2020_.svg.png",
|
|
582
480
|
parameters,
|
|
583
481
|
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
482
|
+
categories: ["productivity"],
|
|
584
483
|
onboarding: gmailOnboarding,
|
|
585
484
|
systemPrompt: {
|
|
586
485
|
en: `### Tools
|
|
587
486
|
|
|
588
|
-
- \`gmail-service-
|
|
487
|
+
- \`gmail-service-account_request_with_delegation\`: Call the Gmail API on behalf of the specified Workspace user via Domain-wide Delegation. Pass \`subject\` as the target user's email; the token will be issued as that user. Always pass \`scopes\`.
|
|
488
|
+
|
|
489
|
+
### OAuth Scopes (pass as \`scopes\` argument)
|
|
490
|
+
|
|
491
|
+
This connector is currently read-only. Pass:
|
|
492
|
+
|
|
493
|
+
- \`https://www.googleapis.com/auth/gmail.readonly\` \u2014 read-only access (profile, labels, messages, threads)
|
|
494
|
+
|
|
495
|
+
The Workspace admin must have authorized this scope for the service account in the Domain-wide Delegation settings, otherwise token issuance will fail with \`unauthorized_client\`.
|
|
496
|
+
|
|
497
|
+
Per-endpoint scope reference: https://developers.google.com/gmail/api/auth/scopes
|
|
589
498
|
|
|
590
499
|
### Gmail API Reference
|
|
591
500
|
|
|
592
501
|
#### Available Endpoints
|
|
593
|
-
- GET \`/me/profile\` \u2014 Get the
|
|
502
|
+
- GET \`/me/profile\` \u2014 Get the impersonated user's profile (email address, total messages/threads)
|
|
594
503
|
- GET \`/me/labels\` \u2014 List all labels in the mailbox
|
|
595
504
|
- GET \`/me/labels/{id}\` \u2014 Get details for a specific label
|
|
596
505
|
- GET \`/me/messages\` \u2014 List messages (returns IDs only; use format param on individual messages)
|
|
@@ -615,7 +524,7 @@ var gmailConnector = new ConnectorPlugin({
|
|
|
615
524
|
- \`raw\` \u2014 Full RFC 2822 formatted message in base64url
|
|
616
525
|
|
|
617
526
|
#### Tips
|
|
618
|
-
- Always use \`/me\` as the userId \u2014 it refers to the
|
|
527
|
+
- Always use \`/me\` as the userId in the path \u2014 it refers to the user you passed as \`subject\`
|
|
619
528
|
- List endpoints return only IDs; fetch individual resources for details
|
|
620
529
|
- Use \`format=metadata\` to efficiently get subject/sender without full body
|
|
621
530
|
- Message body content is base64url encoded in \`payload.body.data\` or nested \`payload.parts[].body.data\`
|
|
@@ -626,6 +535,12 @@ var gmailConnector = new ConnectorPlugin({
|
|
|
626
535
|
|
|
627
536
|
The business logic type for this connector is "typescript". Write handler code using the connector SDK shown below. Do NOT access credentials directly from environment variables.
|
|
628
537
|
|
|
538
|
+
SDK methods (client created via \`connection(connectionId)\`):
|
|
539
|
+
|
|
540
|
+
- \`client.requestWithDelegation(path, { subject, scopes, init? })\` \u2014 call the Gmail API as the impersonated Workspace user via Domain-wide Delegation. Pass the minimum scopes required by the endpoint.
|
|
541
|
+
|
|
542
|
+
The method returns a standard \`Response\`. Read the body with \`.json()\`. Same path conventions as the tool.
|
|
543
|
+
|
|
629
544
|
#### Example
|
|
630
545
|
|
|
631
546
|
\`\`\`ts
|
|
@@ -633,37 +548,51 @@ import { connection } from "@squadbase/vite-server/connectors/gmail";
|
|
|
633
548
|
|
|
634
549
|
const gmail = connection("<connectionId>");
|
|
635
550
|
|
|
636
|
-
//
|
|
637
|
-
const
|
|
551
|
+
// The user to impersonate.
|
|
552
|
+
const subject = "alice@example.com";
|
|
553
|
+
const READ = ["https://www.googleapis.com/auth/gmail.readonly"];
|
|
554
|
+
|
|
555
|
+
const profileRes = await gmail.requestWithDelegation("/me/profile", {
|
|
556
|
+
subject,
|
|
557
|
+
scopes: READ,
|
|
558
|
+
});
|
|
559
|
+
const profile = await profileRes.json();
|
|
638
560
|
console.log(profile.emailAddress, profile.messagesTotal);
|
|
639
561
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
562
|
+
const messagesRes = await gmail.requestWithDelegation(
|
|
563
|
+
"/me/messages?maxResults=10",
|
|
564
|
+
{ subject, scopes: READ },
|
|
565
|
+
);
|
|
566
|
+
const messages = await messagesRes.json();
|
|
567
|
+
|
|
568
|
+
for (const msg of messages.messages ?? []) {
|
|
569
|
+
const detailRes = await gmail.requestWithDelegation(
|
|
570
|
+
\`/me/messages/\${msg.id}?format=metadata\`,
|
|
571
|
+
{ subject, scopes: READ },
|
|
572
|
+
);
|
|
573
|
+
const detail = await detailRes.json();
|
|
574
|
+
const subjectHeader = detail.payload.headers.find(h => h.name === "Subject")?.value;
|
|
575
|
+
console.log(subjectHeader, detail.snippet);
|
|
646
576
|
}
|
|
577
|
+
\`\`\``,
|
|
578
|
+
ja: `### \u30C4\u30FC\u30EB
|
|
647
579
|
|
|
648
|
-
|
|
649
|
-
const results = await gmail.listMessages({ q: "from:boss@company.com is:unread" });
|
|
580
|
+
- \`gmail-service-account_request_with_delegation\`: \u6307\u5B9A\u3055\u308C\u305F Workspace \u30E6\u30FC\u30B6\u30FC\u306B\u4EE3\u308F\u3063\u3066 Domain-wide Delegation \u7D4C\u7531\u3067 Gmail API \u3092\u547C\u3073\u51FA\u3057\u307E\u3059\u3002\u4EE3\u7406\u5BFE\u8C61\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092 \`subject\` \u3068\u3057\u3066\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30C8\u30FC\u30AF\u30F3\u306F\u305D\u306E\u30E6\u30FC\u30B6\u30FC\u3068\u3057\u3066\u767A\u884C\u3055\u308C\u307E\u3059\u3002\`scopes\` \u3082\u6BCE\u56DE\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
650
581
|
|
|
651
|
-
|
|
652
|
-
const labels = await gmail.listLabels();
|
|
653
|
-
labels.labels.forEach(l => console.log(l.name, l.messagesTotal));
|
|
582
|
+
### OAuth \u30B9\u30B3\u30FC\u30D7 (\`scopes\` \u5F15\u6570\u3067\u6E21\u3059)
|
|
654
583
|
|
|
655
|
-
|
|
656
|
-
const thread = await gmail.getThread("<threadId>");
|
|
657
|
-
thread.messages.forEach(m => console.log(m.snippet));
|
|
658
|
-
\`\`\``,
|
|
659
|
-
ja: `### \u30C4\u30FC\u30EB
|
|
584
|
+
\u3053\u306E\u30B3\u30CD\u30AF\u30BF\u30FC\u306F\u73FE\u72B6\u8AAD\u307F\u53D6\u308A\u5C02\u7528\u3067\u3059\u3002\u6B21\u3092\u6E21\u3057\u3066\u304F\u3060\u3055\u3044:
|
|
660
585
|
|
|
661
|
-
- \`gmail
|
|
586
|
+
- \`https://www.googleapis.com/auth/gmail.readonly\` \u2014 \u8AAD\u307F\u53D6\u308A\uFF08\u30D7\u30ED\u30D5\u30A3\u30FC\u30EB\u3001\u30E9\u30D9\u30EB\u3001\u30E1\u30C3\u30BB\u30FC\u30B8\u3001\u30B9\u30EC\u30C3\u30C9\uFF09
|
|
587
|
+
|
|
588
|
+
\u8981\u6C42\u3059\u308B scope \u306F Workspace \u7BA1\u7406\u8005\u304C Domain-wide Delegation \u8A2D\u5B9A\u3067\u5F53\u8A72 Service Account \u306B\u5BFE\u3057\u3066\u627F\u8A8D\u3057\u3066\u3044\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002\u627F\u8A8D\u3055\u308C\u3066\u3044\u306A\u3044\u5834\u5408\u30C8\u30FC\u30AF\u30F3\u767A\u884C\u304C \`unauthorized_client\` \u3067\u5931\u6557\u3057\u307E\u3059\u3002
|
|
589
|
+
|
|
590
|
+
\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u5225\u306E\u6B63\u78BA\u306A scope \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9: https://developers.google.com/gmail/api/auth/scopes
|
|
662
591
|
|
|
663
592
|
### Gmail API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
|
|
664
593
|
|
|
665
594
|
#### \u5229\u7528\u53EF\u80FD\u306A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8
|
|
666
|
-
- GET \`/me/profile\` \u2014 \
|
|
595
|
+
- GET \`/me/profile\` \u2014 \u4EE3\u7406\u5BFE\u8C61\u30E6\u30FC\u30B6\u30FC\u306E\u30D7\u30ED\u30D5\u30A3\u30FC\u30EB\u3092\u53D6\u5F97\uFF08\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3001\u30E1\u30C3\u30BB\u30FC\u30B8/\u30B9\u30EC\u30C3\u30C9\u7DCF\u6570\uFF09
|
|
667
596
|
- GET \`/me/labels\` \u2014 \u30E1\u30FC\u30EB\u30DC\u30C3\u30AF\u30B9\u306E\u5168\u30E9\u30D9\u30EB\u3092\u4E00\u89A7
|
|
668
597
|
- GET \`/me/labels/{id}\` \u2014 \u7279\u5B9A\u30E9\u30D9\u30EB\u306E\u8A73\u7D30\u3092\u53D6\u5F97
|
|
669
598
|
- GET \`/me/messages\` \u2014 \u30E1\u30C3\u30BB\u30FC\u30B8\u4E00\u89A7\uFF08ID\u306E\u307F\u8FD4\u5374\u3002\u500B\u5225\u30E1\u30C3\u30BB\u30FC\u30B8\u3067format\u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u4F7F\u7528\uFF09
|
|
@@ -688,7 +617,7 @@ thread.messages.forEach(m => console.log(m.snippet));
|
|
|
688
617
|
- \`raw\` \u2014 base64url\u30A8\u30F3\u30B3\u30FC\u30C9\u3055\u308C\u305F\u5B8C\u5168\u306ARFC 2822\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8\u30E1\u30C3\u30BB\u30FC\u30B8
|
|
689
618
|
|
|
690
619
|
#### \u30D2\u30F3\u30C8
|
|
691
|
-
- userId\u306B\u306F\u5E38\u306B \`/me\` \u3092\u4F7F\u7528 \u2014 \
|
|
620
|
+
- \u30D1\u30B9\u306E userId \u306B\u306F\u5E38\u306B \`/me\` \u3092\u4F7F\u7528 \u2014 \`subject\` \u306B\u6E21\u3057\u305F\u30E6\u30FC\u30B6\u30FC\u3092\u6307\u3057\u307E\u3059
|
|
692
621
|
- \u4E00\u89A7\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u306FID\u306E\u307F\u8FD4\u5374\u3057\u307E\u3059\u3002\u8A73\u7D30\u306F\u500B\u5225\u30EA\u30BD\u30FC\u30B9\u3092\u53D6\u5F97\u3057\u3066\u304F\u3060\u3055\u3044
|
|
693
622
|
- \u4EF6\u540D/\u9001\u4FE1\u8005\u3092\u52B9\u7387\u7684\u306B\u53D6\u5F97\u3059\u308B\u306B\u306F \`format=metadata\` \u3092\u4F7F\u7528\u3057\u307E\u3059
|
|
694
623
|
- \u30E1\u30C3\u30BB\u30FC\u30B8\u672C\u6587\u306F \`payload.body.data\` \u307E\u305F\u306F\u30CD\u30B9\u30C8\u3055\u308C\u305F \`payload.parts[].body.data\` \u306Bbase64url\u30A8\u30F3\u30B3\u30FC\u30C9\u3067\u683C\u7D0D\u3055\u308C\u3066\u3044\u307E\u3059
|
|
@@ -699,6 +628,12 @@ thread.messages.forEach(m => console.log(m.snippet));
|
|
|
699
628
|
|
|
700
629
|
\u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u4EE5\u4E0B\u306B\u793A\u3059\u30B3\u30CD\u30AF\u30BFSDK\u3092\u4F7F\u7528\u3057\u3066\u30CF\u30F3\u30C9\u30E9\u30B3\u30FC\u30C9\u3092\u8A18\u8FF0\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u76F4\u63A5\u8A8D\u8A3C\u60C5\u5831\u306B\u30A2\u30AF\u30BB\u30B9\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
|
|
701
630
|
|
|
631
|
+
SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
|
|
632
|
+
|
|
633
|
+
- \`client.requestWithDelegation(path, { subject, scopes, init? })\` \u2014 Domain-wide Delegation \u3067\u6307\u5B9A Workspace \u30E6\u30FC\u30B6\u30FC\u306B\u4EE3\u308F\u3063\u3066 Gmail API \u3092\u547C\u3076\u3002\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u306B\u5FC5\u8981\u306A\u6700\u5C0F scope \u3092\u6E21\u3059\u3002
|
|
634
|
+
|
|
635
|
+
\u30E1\u30BD\u30C3\u30C9\u306F\u6A19\u6E96\u306E \`Response\` \u3092\u8FD4\u3057\u307E\u3059\u3002\`response.json()\` \u3067\u30DC\u30C7\u30A3\u3092\u53D6\u5F97\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30D1\u30B9\u306E\u66F8\u304D\u65B9\u306F\u30C4\u30FC\u30EB\u3068\u540C\u3058\u3002
|
|
636
|
+
|
|
702
637
|
#### Example
|
|
703
638
|
|
|
704
639
|
\`\`\`ts
|
|
@@ -706,86 +641,35 @@ import { connection } from "@squadbase/vite-server/connectors/gmail";
|
|
|
706
641
|
|
|
707
642
|
const gmail = connection("<connectionId>");
|
|
708
643
|
|
|
709
|
-
//
|
|
710
|
-
const
|
|
711
|
-
|
|
644
|
+
// \u4EE3\u7406\u5BFE\u8C61\u30E6\u30FC\u30B6\u30FC
|
|
645
|
+
const subject = "alice@example.com";
|
|
646
|
+
const READ = ["https://www.googleapis.com/auth/gmail.readonly"];
|
|
712
647
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
// Search messages
|
|
722
|
-
const results = await gmail.listMessages({ q: "from:boss@company.com is:unread" });
|
|
648
|
+
const profileRes = await gmail.requestWithDelegation("/me/profile", {
|
|
649
|
+
subject,
|
|
650
|
+
scopes: READ,
|
|
651
|
+
});
|
|
652
|
+
const profile = await profileRes.json();
|
|
653
|
+
console.log(profile.emailAddress, profile.messagesTotal);
|
|
723
654
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
655
|
+
const messagesRes = await gmail.requestWithDelegation(
|
|
656
|
+
"/me/messages?maxResults=10",
|
|
657
|
+
{ subject, scopes: READ },
|
|
658
|
+
);
|
|
659
|
+
const messages = await messagesRes.json();
|
|
727
660
|
|
|
728
|
-
|
|
729
|
-
const
|
|
730
|
-
|
|
661
|
+
for (const msg of messages.messages ?? []) {
|
|
662
|
+
const detailRes = await gmail.requestWithDelegation(
|
|
663
|
+
\`/me/messages/\${msg.id}?format=metadata\`,
|
|
664
|
+
{ subject, scopes: READ },
|
|
665
|
+
);
|
|
666
|
+
const detail = await detailRes.json();
|
|
667
|
+
const subjectHeader = detail.payload.headers.find(h => h.name === "Subject")?.value;
|
|
668
|
+
console.log(subjectHeader, detail.snippet);
|
|
669
|
+
}
|
|
731
670
|
\`\`\``
|
|
732
671
|
},
|
|
733
|
-
tools
|
|
734
|
-
async checkConnection(params, _config) {
|
|
735
|
-
const { GoogleAuth } = await import("google-auth-library");
|
|
736
|
-
const credentials = JSON.parse(
|
|
737
|
-
Buffer.from(
|
|
738
|
-
params[parameters.serviceAccountKeyJsonBase64.slug],
|
|
739
|
-
"base64"
|
|
740
|
-
).toString("utf-8")
|
|
741
|
-
);
|
|
742
|
-
const delegatedUserEmail = params[delegatedUserEmailParameter.slug];
|
|
743
|
-
if (!delegatedUserEmail) {
|
|
744
|
-
if (!credentials.client_email || !credentials.private_key) {
|
|
745
|
-
return {
|
|
746
|
-
success: false,
|
|
747
|
-
error: "Service account JSON must contain client_email and private_key"
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
|
-
return { success: true };
|
|
751
|
-
}
|
|
752
|
-
const auth = new GoogleAuth({
|
|
753
|
-
credentials,
|
|
754
|
-
scopes: ["https://www.googleapis.com/auth/gmail.readonly"],
|
|
755
|
-
clientOptions: {
|
|
756
|
-
subject: delegatedUserEmail
|
|
757
|
-
}
|
|
758
|
-
});
|
|
759
|
-
try {
|
|
760
|
-
const token = await auth.getAccessToken();
|
|
761
|
-
if (!token) {
|
|
762
|
-
return {
|
|
763
|
-
success: false,
|
|
764
|
-
error: "Failed to obtain access token"
|
|
765
|
-
};
|
|
766
|
-
}
|
|
767
|
-
const res = await fetch(
|
|
768
|
-
"https://gmail.googleapis.com/gmail/v1/users/me/profile",
|
|
769
|
-
{
|
|
770
|
-
method: "GET",
|
|
771
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
772
|
-
}
|
|
773
|
-
);
|
|
774
|
-
if (!res.ok) {
|
|
775
|
-
const errorText = await res.text().catch(() => res.statusText);
|
|
776
|
-
return {
|
|
777
|
-
success: false,
|
|
778
|
-
error: `Gmail API failed: HTTP ${res.status} ${errorText}`
|
|
779
|
-
};
|
|
780
|
-
}
|
|
781
|
-
return { success: true };
|
|
782
|
-
} catch (error) {
|
|
783
|
-
return {
|
|
784
|
-
success: false,
|
|
785
|
-
error: error instanceof Error ? error.message : String(error)
|
|
786
|
-
};
|
|
787
|
-
}
|
|
788
|
-
}
|
|
672
|
+
tools
|
|
789
673
|
});
|
|
790
674
|
|
|
791
675
|
// src/connectors/create-connector-sdk.ts
|