@squadbase/vite-server 0.1.8-dev.d378524 → 0.1.8
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 +749 -617
- package/dist/cli/{sshcrypto-VVJOJ3LR.node → sshcrypto-P3UBA7BP.node} +0 -0
- package/dist/connectors/gmail.js +214 -348
- package/dist/connectors/google-calendar.js +477 -449
- package/dist/index.js +749 -617
- package/dist/main.js +749 -617
- package/dist/{sshcrypto-VVJOJ3LR.node → sshcrypto-P3UBA7BP.node} +0 -0
- package/dist/vite-plugin.js +749 -617
- package/package.json +1 -1
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
|
|
|
@@ -411,22 +253,33 @@ var AUTH_TYPES = {
|
|
|
411
253
|
USER_PASSWORD: "user-password"
|
|
412
254
|
};
|
|
413
255
|
|
|
414
|
-
// ../connectors/src/connectors/gmail/tools/request.ts
|
|
256
|
+
// ../connectors/src/connectors/gmail/tools/request-with-delegation.ts
|
|
415
257
|
import { z } from "zod";
|
|
416
258
|
var BASE_URL2 = "https://gmail.googleapis.com/gmail/v1/users";
|
|
417
259
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
260
|
+
function decodeServiceAccount(keyJsonBase64) {
|
|
261
|
+
const decoded = Buffer.from(keyJsonBase64, "base64").toString("utf-8");
|
|
262
|
+
return JSON.parse(decoded);
|
|
263
|
+
}
|
|
418
264
|
var inputSchema = z.object({
|
|
419
265
|
toolUseIntent: z.string().optional().describe(
|
|
420
266
|
"Brief description of what you intend to accomplish with this tool call"
|
|
421
267
|
),
|
|
422
268
|
connectionId: z.string().describe("ID of the Gmail service account connection to use"),
|
|
423
|
-
method: z.enum(["GET"]).describe("HTTP method
|
|
269
|
+
method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).describe("HTTP method"),
|
|
424
270
|
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."
|
|
271
|
+
"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."
|
|
272
|
+
),
|
|
273
|
+
subject: z.string().describe(
|
|
274
|
+
"Email of the Workspace user to impersonate via Domain-wide Delegation. The token will be issued as this user."
|
|
275
|
+
),
|
|
276
|
+
scopes: z.array(z.string()).describe(
|
|
277
|
+
"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
278
|
),
|
|
427
279
|
queryParams: z.record(z.string(), z.string()).optional().describe(
|
|
428
280
|
"Query parameters to append to the URL (e.g., { q: 'from:example@gmail.com', maxResults: '10' })"
|
|
429
|
-
)
|
|
281
|
+
),
|
|
282
|
+
body: z.record(z.string(), z.unknown()).optional().describe("JSON request body for POST/PUT/PATCH")
|
|
430
283
|
});
|
|
431
284
|
var outputSchema = z.discriminatedUnion("success", [
|
|
432
285
|
z.object({
|
|
@@ -436,17 +289,16 @@ var outputSchema = z.discriminatedUnion("success", [
|
|
|
436
289
|
}),
|
|
437
290
|
z.object({
|
|
438
291
|
success: z.literal(false),
|
|
439
|
-
error: z.string()
|
|
292
|
+
error: z.string(),
|
|
293
|
+
serviceAccountEmail: z.string().optional()
|
|
440
294
|
})
|
|
441
295
|
]);
|
|
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').`,
|
|
296
|
+
var requestWithDelegationTool = new ConnectorTool({
|
|
297
|
+
name: "request_with_delegation",
|
|
298
|
+
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
299
|
inputSchema,
|
|
448
300
|
outputSchema,
|
|
449
|
-
async execute({ connectionId, method, path: path2, queryParams }, connections) {
|
|
301
|
+
async execute({ connectionId, method, path: path2, subject, scopes, queryParams, body }, connections) {
|
|
450
302
|
const connection2 = connections.find((c) => c.id === connectionId);
|
|
451
303
|
if (!connection2) {
|
|
452
304
|
return {
|
|
@@ -454,28 +306,37 @@ All paths are relative to https://gmail.googleapis.com/gmail/v1/users. Use '/me'
|
|
|
454
306
|
error: `Connection ${connectionId} not found`
|
|
455
307
|
};
|
|
456
308
|
}
|
|
309
|
+
const keyJsonBase64 = parameters.serviceAccountKeyJsonBase64.getValue(connection2);
|
|
310
|
+
let serviceAccount;
|
|
311
|
+
try {
|
|
312
|
+
serviceAccount = decodeServiceAccount(keyJsonBase64);
|
|
313
|
+
} catch (err) {
|
|
314
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
315
|
+
return {
|
|
316
|
+
success: false,
|
|
317
|
+
error: `Failed to decode service account key: ${msg}`
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
const serviceAccountEmail = serviceAccount.client_email;
|
|
457
321
|
console.log(
|
|
458
|
-
`[connector-request] gmail/${connection2.name}: ${method} ${path2}`
|
|
322
|
+
`[connector-request] gmail/${connection2.name}: ${method} ${path2} subject=${subject}`
|
|
459
323
|
);
|
|
460
324
|
try {
|
|
461
325
|
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
326
|
const auth = new GoogleAuth({
|
|
468
|
-
credentials
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
327
|
+
credentials: {
|
|
328
|
+
client_email: serviceAccount.client_email,
|
|
329
|
+
private_key: serviceAccount.private_key
|
|
330
|
+
},
|
|
331
|
+
scopes,
|
|
332
|
+
clientOptions: { subject }
|
|
473
333
|
});
|
|
474
334
|
const token = await auth.getAccessToken();
|
|
475
335
|
if (!token) {
|
|
476
336
|
return {
|
|
477
337
|
success: false,
|
|
478
|
-
error: "Failed to obtain access token"
|
|
338
|
+
error: "Failed to obtain access token",
|
|
339
|
+
serviceAccountEmail
|
|
479
340
|
};
|
|
480
341
|
}
|
|
481
342
|
let url = `${BASE_URL2}${path2.startsWith("/") ? "" : "/"}${path2}`;
|
|
@@ -486,18 +347,25 @@ All paths are relative to https://gmail.googleapis.com/gmail/v1/users. Use '/me'
|
|
|
486
347
|
const controller = new AbortController();
|
|
487
348
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
488
349
|
try {
|
|
350
|
+
const hasBody = body != null && ["POST", "PUT", "PATCH"].includes(method);
|
|
489
351
|
const response = await fetch(url, {
|
|
490
352
|
method,
|
|
491
353
|
headers: {
|
|
492
354
|
Authorization: `Bearer ${token}`,
|
|
493
355
|
"Content-Type": "application/json"
|
|
494
356
|
},
|
|
357
|
+
body: hasBody ? JSON.stringify(body) : void 0,
|
|
495
358
|
signal: controller.signal
|
|
496
359
|
});
|
|
497
|
-
const data = await response.json();
|
|
360
|
+
const data = await response.json().catch(() => ({}));
|
|
498
361
|
if (!response.ok) {
|
|
499
|
-
const
|
|
500
|
-
|
|
362
|
+
const errorObj = data?.error;
|
|
363
|
+
const errorMessage = errorObj?.message ?? (typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`);
|
|
364
|
+
return {
|
|
365
|
+
success: false,
|
|
366
|
+
error: errorMessage,
|
|
367
|
+
serviceAccountEmail
|
|
368
|
+
};
|
|
501
369
|
}
|
|
502
370
|
return { success: true, status: response.status, data };
|
|
503
371
|
} finally {
|
|
@@ -505,79 +373,92 @@ All paths are relative to https://gmail.googleapis.com/gmail/v1/users. Use '/me'
|
|
|
505
373
|
}
|
|
506
374
|
} catch (err) {
|
|
507
375
|
const msg = err instanceof Error ? err.message : String(err);
|
|
508
|
-
return {
|
|
376
|
+
return {
|
|
377
|
+
success: false,
|
|
378
|
+
error: msg,
|
|
379
|
+
serviceAccountEmail
|
|
380
|
+
};
|
|
509
381
|
}
|
|
510
382
|
}
|
|
511
383
|
});
|
|
512
384
|
|
|
513
385
|
// ../connectors/src/connectors/gmail/setup.ts
|
|
514
|
-
var
|
|
386
|
+
var requestWithDelegationToolName = `gmail-service-account_${requestWithDelegationTool.name}`;
|
|
387
|
+
var READONLY_SCOPES = '["https://www.googleapis.com/auth/gmail.readonly"]';
|
|
515
388
|
var gmailOnboarding = new ConnectorOnboarding({
|
|
516
389
|
connectionSetupInstructions: {
|
|
517
|
-
ja:
|
|
390
|
+
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
391
|
|
|
519
|
-
1. \`askUserQuestion\` \u3067\
|
|
392
|
+
1. \`askUserQuestion\` \u3067\u5BFE\u8C61\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u805E\u304F:
|
|
520
393
|
- \`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:
|
|
394
|
+
- \`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
|
|
395
|
+
- \`placeholder\`: \`"alice@example.com, bob@example.com"\`
|
|
396
|
+
|
|
397
|
+
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
398
|
- \`method\`: \`"GET"\`
|
|
528
399
|
- \`path\`: \`"/me/profile"\`
|
|
529
|
-
|
|
530
|
-
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
-
|
|
535
|
-
-
|
|
400
|
+
- \`subject\`: \`<email>\`
|
|
401
|
+
- \`scopes\`: \`${READONLY_SCOPES}\`
|
|
402
|
+
|
|
403
|
+
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
|
|
404
|
+
- \`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" }]\`
|
|
405
|
+
- \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
|
|
406
|
+
- \u300C\u5165\u529B\u3057\u76F4\u3059\u300D: \u30B9\u30C6\u30C3\u30D7 1 \u304B\u3089\u518D\u5B9F\u884C
|
|
407
|
+
|
|
408
|
+
4. \u5168\u30A2\u30C9\u30EC\u30B9\u304C\u6210\u529F\u3057\u305F\u3089 \`finalizeSetup\` \u3092\u547C\u3076\u3002\`projectKnowledge\` \u306E \`#### \u30B9\u30B3\u30FC\u30D7\` \u7BC0\u306B\u5404\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092 1 \u884C\u305A\u3064\u5217\u6319\u3059\u308B:
|
|
409
|
+
- \`- subject: alice@example.com\`
|
|
410
|
+
- \`- subject: bob@example.com\`
|
|
536
411
|
|
|
537
412
|
#### \u5236\u7D04
|
|
538
|
-
-
|
|
539
|
-
- \u30C4\u30FC\u30EB\u9593\
|
|
540
|
-
en: `
|
|
413
|
+
- \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
|
|
414
|
+
- \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`,
|
|
415
|
+
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
416
|
|
|
542
|
-
1. Call \`askUserQuestion\` to
|
|
417
|
+
1. Call \`askUserQuestion\` to collect target emails:
|
|
543
418
|
- \`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:
|
|
419
|
+
- \`question\`: "Enter the Google Workspace user email(s) whose Gmail mailbox you want to access (comma-separated for multiple)"
|
|
420
|
+
- \`placeholder\`: \`"alice@example.com, bob@example.com"\`
|
|
421
|
+
|
|
422
|
+
2. Extract individual emails from the response. For each \`<email>\`, verify Domain-wide Delegation access by calling \`${requestWithDelegationToolName}\`:
|
|
550
423
|
- \`method\`: \`"GET"\`
|
|
551
424
|
- \`path\`: \`"/me/profile"\`
|
|
552
|
-
|
|
553
|
-
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
-
|
|
558
|
-
-
|
|
425
|
+
- \`subject\`: \`<email>\`
|
|
426
|
+
- \`scopes\`: \`${READONLY_SCOPES}\`
|
|
427
|
+
|
|
428
|
+
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))".
|
|
429
|
+
- \`options\`: \`[{ label: "Authorized Domain-wide Delegation \u2014 retry", value: "retry" }, { label: "Re-enter the email addresses", value: "restart" }]\`
|
|
430
|
+
- On "retry" \u2192 re-run step 2 with the previously entered email list
|
|
431
|
+
- On "Re-enter" \u2192 re-run step 1
|
|
432
|
+
|
|
433
|
+
4. Once every email succeeds, call \`finalizeSetup\`. Under \`#### \u30B9\u30B3\u30FC\u30D7\` in \`projectKnowledge\`, list each email on its own line:
|
|
434
|
+
- \`- subject: alice@example.com\`
|
|
435
|
+
- \`- subject: bob@example.com\`
|
|
559
436
|
|
|
560
437
|
#### Constraints
|
|
561
|
-
-
|
|
562
|
-
- Write
|
|
438
|
+
- Do NOT read message bodies during setup. Only the \`/me/profile\` verification is permitted
|
|
439
|
+
- Write at most 1 sentence between tool calls`
|
|
563
440
|
},
|
|
564
441
|
dataOverviewInstructions: {
|
|
565
|
-
en: `
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
442
|
+
en: `Pick ONE email from \`#### \u30B9\u30B3\u30FC\u30D7\` (a \`- subject: <email>\` line) and use it as \`subject\` for every call below. Pass \`scopes: ${READONLY_SCOPES}\` every time.
|
|
443
|
+
|
|
444
|
+
1. \`method=GET\`, \`path=/me/labels\` to list labels.
|
|
445
|
+
2. \`method=GET\`, \`path=/me/messages?maxResults=5\` to fetch recent message IDs.
|
|
446
|
+
3. For each message id, \`method=GET\`, \`path=/me/messages/{id}?format=metadata\`.`,
|
|
447
|
+
ja: `\`#### \u30B9\u30B3\u30FC\u30D7\` \u306E \`- subject: <email>\` \u884C\u304B\u3089 1 \u3064\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
|
|
448
|
+
|
|
449
|
+
1. \`method=GET\`\u3001\`path=/me/labels\` \u3067\u30E9\u30D9\u30EB\u4E00\u89A7\u3092\u53D6\u5F97
|
|
450
|
+
2. \`method=GET\`\u3001\`path=/me/messages?maxResults=5\` \u3067\u6700\u8FD1\u306E\u30E1\u30C3\u30BB\u30FC\u30B8 ID \u3092\u53D6\u5F97
|
|
451
|
+
3. \u5404\u30E1\u30C3\u30BB\u30FC\u30B8 ID \u306B\u3064\u3044\u3066 \`method=GET\`\u3001\`path=/me/messages/{id}?format=metadata\``
|
|
571
452
|
}
|
|
572
453
|
});
|
|
573
454
|
|
|
574
455
|
// ../connectors/src/connectors/gmail/index.ts
|
|
575
|
-
var tools = {
|
|
456
|
+
var tools = { request_with_delegation: requestWithDelegationTool };
|
|
576
457
|
var gmailConnector = new ConnectorPlugin({
|
|
577
458
|
slug: "gmail",
|
|
578
459
|
authType: AUTH_TYPES.SERVICE_ACCOUNT,
|
|
579
460
|
name: "Gmail",
|
|
580
|
-
description: "Connect to Gmail for email data access using a service account with domain-wide delegation.
|
|
461
|
+
description: "Connect to Gmail for email data access using a service account with domain-wide delegation. Currently read-only (messages, threads, labels).",
|
|
581
462
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/4V3rfaSc1ksFIt2eHBNIwJ/7f3be41a154a6d96dcf229ed0e5858c9/Gmail_icon__2020_.svg.png",
|
|
582
463
|
parameters,
|
|
583
464
|
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
@@ -585,12 +466,22 @@ var gmailConnector = new ConnectorPlugin({
|
|
|
585
466
|
systemPrompt: {
|
|
586
467
|
en: `### Tools
|
|
587
468
|
|
|
588
|
-
- \`gmail-service-
|
|
469
|
+
- \`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 email; the token will be issued as that user. The set of users this connection works with is recorded in the project knowledge under "#### \u30B9\u30B3\u30FC\u30D7" as \`- subject: <email>\` lines \u2014 read those and pick a subject before calling. Always pass \`scopes\`.
|
|
470
|
+
|
|
471
|
+
### OAuth Scopes (pass as \`scopes\` argument)
|
|
472
|
+
|
|
473
|
+
This connector is currently read-only. Pass:
|
|
474
|
+
|
|
475
|
+
- \`https://www.googleapis.com/auth/gmail.readonly\` \u2014 read-only access (profile, labels, messages, threads)
|
|
476
|
+
|
|
477
|
+
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\`.
|
|
478
|
+
|
|
479
|
+
Per-endpoint scope reference: https://developers.google.com/gmail/api/auth/scopes
|
|
589
480
|
|
|
590
481
|
### Gmail API Reference
|
|
591
482
|
|
|
592
483
|
#### Available Endpoints
|
|
593
|
-
- GET \`/me/profile\` \u2014 Get the
|
|
484
|
+
- GET \`/me/profile\` \u2014 Get the impersonated user's profile (email address, total messages/threads)
|
|
594
485
|
- GET \`/me/labels\` \u2014 List all labels in the mailbox
|
|
595
486
|
- GET \`/me/labels/{id}\` \u2014 Get details for a specific label
|
|
596
487
|
- GET \`/me/messages\` \u2014 List messages (returns IDs only; use format param on individual messages)
|
|
@@ -615,7 +506,7 @@ var gmailConnector = new ConnectorPlugin({
|
|
|
615
506
|
- \`raw\` \u2014 Full RFC 2822 formatted message in base64url
|
|
616
507
|
|
|
617
508
|
#### Tips
|
|
618
|
-
- Always use \`/me\` as the userId \u2014 it refers to the
|
|
509
|
+
- Always use \`/me\` as the userId in the path \u2014 it refers to the user you passed as \`subject\`
|
|
619
510
|
- List endpoints return only IDs; fetch individual resources for details
|
|
620
511
|
- Use \`format=metadata\` to efficiently get subject/sender without full body
|
|
621
512
|
- Message body content is base64url encoded in \`payload.body.data\` or nested \`payload.parts[].body.data\`
|
|
@@ -626,6 +517,12 @@ var gmailConnector = new ConnectorPlugin({
|
|
|
626
517
|
|
|
627
518
|
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
519
|
|
|
520
|
+
SDK methods (client created via \`connection(connectionId)\`):
|
|
521
|
+
|
|
522
|
+
- \`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.
|
|
523
|
+
|
|
524
|
+
The method returns a standard \`Response\`. Read the body with \`.json()\`. Same path conventions as the tool.
|
|
525
|
+
|
|
629
526
|
#### Example
|
|
630
527
|
|
|
631
528
|
\`\`\`ts
|
|
@@ -633,37 +530,51 @@ import { connection } from "@squadbase/vite-server/connectors/gmail";
|
|
|
633
530
|
|
|
634
531
|
const gmail = connection("<connectionId>");
|
|
635
532
|
|
|
636
|
-
//
|
|
637
|
-
const
|
|
533
|
+
// Pick the impersonated user from project knowledge ("#### \u30B9\u30B3\u30FC\u30D7").
|
|
534
|
+
const subject = "alice@example.com";
|
|
535
|
+
const READ = ["https://www.googleapis.com/auth/gmail.readonly"];
|
|
536
|
+
|
|
537
|
+
const profileRes = await gmail.requestWithDelegation("/me/profile", {
|
|
538
|
+
subject,
|
|
539
|
+
scopes: READ,
|
|
540
|
+
});
|
|
541
|
+
const profile = await profileRes.json();
|
|
638
542
|
console.log(profile.emailAddress, profile.messagesTotal);
|
|
639
543
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
544
|
+
const messagesRes = await gmail.requestWithDelegation(
|
|
545
|
+
"/me/messages?maxResults=10",
|
|
546
|
+
{ subject, scopes: READ },
|
|
547
|
+
);
|
|
548
|
+
const messages = await messagesRes.json();
|
|
549
|
+
|
|
550
|
+
for (const msg of messages.messages ?? []) {
|
|
551
|
+
const detailRes = await gmail.requestWithDelegation(
|
|
552
|
+
\`/me/messages/\${msg.id}?format=metadata\`,
|
|
553
|
+
{ subject, scopes: READ },
|
|
554
|
+
);
|
|
555
|
+
const detail = await detailRes.json();
|
|
556
|
+
const subjectHeader = detail.payload.headers.find(h => h.name === "Subject")?.value;
|
|
557
|
+
console.log(subjectHeader, detail.snippet);
|
|
646
558
|
}
|
|
559
|
+
\`\`\``,
|
|
560
|
+
ja: `### \u30C4\u30FC\u30EB
|
|
647
561
|
|
|
648
|
-
|
|
649
|
-
const results = await gmail.listMessages({ q: "from:boss@company.com is:unread" });
|
|
562
|
+
- \`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\u3053\u306E\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u304C\u5BFE\u8C61\u3068\u3059\u308B\u30E6\u30FC\u30B6\u30FC\u306F Project Knowledge \u306E "#### \u30B9\u30B3\u30FC\u30D7" \u306B \`- subject: <email>\` \u5F62\u5F0F\u3067\u8A18\u9332\u3055\u308C\u3066\u3044\u308B\u305F\u3081\u3001\u547C\u3073\u51FA\u3057\u524D\u306B\u305D\u3053\u3092\u8AAD\u3093\u3067 subject \u3092\u6C7A\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002\`scopes\` \u3082\u6BCE\u56DE\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
650
563
|
|
|
651
|
-
|
|
652
|
-
const labels = await gmail.listLabels();
|
|
653
|
-
labels.labels.forEach(l => console.log(l.name, l.messagesTotal));
|
|
564
|
+
### OAuth \u30B9\u30B3\u30FC\u30D7 (\`scopes\` \u5F15\u6570\u3067\u6E21\u3059)
|
|
654
565
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
\`\`\``,
|
|
659
|
-
ja: `### \u30C4\u30FC\u30EB
|
|
566
|
+
\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:
|
|
567
|
+
|
|
568
|
+
- \`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
|
|
660
569
|
|
|
661
|
-
|
|
570
|
+
\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
|
|
571
|
+
|
|
572
|
+
\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
573
|
|
|
663
574
|
### Gmail API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
|
|
664
575
|
|
|
665
576
|
#### \u5229\u7528\u53EF\u80FD\u306A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8
|
|
666
|
-
- GET \`/me/profile\` \u2014 \
|
|
577
|
+
- 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
578
|
- GET \`/me/labels\` \u2014 \u30E1\u30FC\u30EB\u30DC\u30C3\u30AF\u30B9\u306E\u5168\u30E9\u30D9\u30EB\u3092\u4E00\u89A7
|
|
668
579
|
- GET \`/me/labels/{id}\` \u2014 \u7279\u5B9A\u30E9\u30D9\u30EB\u306E\u8A73\u7D30\u3092\u53D6\u5F97
|
|
669
580
|
- 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 +599,7 @@ thread.messages.forEach(m => console.log(m.snippet));
|
|
|
688
599
|
- \`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
600
|
|
|
690
601
|
#### \u30D2\u30F3\u30C8
|
|
691
|
-
- userId\u306B\u306F\u5E38\u306B \`/me\` \u3092\u4F7F\u7528 \u2014 \
|
|
602
|
+
- \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
603
|
- \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
604
|
- \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
605
|
- \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 +610,12 @@ thread.messages.forEach(m => console.log(m.snippet));
|
|
|
699
610
|
|
|
700
611
|
\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
612
|
|
|
613
|
+
SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
|
|
614
|
+
|
|
615
|
+
- \`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
|
|
616
|
+
|
|
617
|
+
\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
|
|
618
|
+
|
|
702
619
|
#### Example
|
|
703
620
|
|
|
704
621
|
\`\`\`ts
|
|
@@ -706,86 +623,35 @@ import { connection } from "@squadbase/vite-server/connectors/gmail";
|
|
|
706
623
|
|
|
707
624
|
const gmail = connection("<connectionId>");
|
|
708
625
|
|
|
709
|
-
//
|
|
710
|
-
const
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
// List recent messages
|
|
714
|
-
const messages = await gmail.listMessages({ maxResults: 10 });
|
|
715
|
-
for (const msg of messages.messages) {
|
|
716
|
-
const detail = await gmail.getMessage(msg.id, "metadata");
|
|
717
|
-
const subject = detail.payload.headers.find(h => h.name === "Subject")?.value;
|
|
718
|
-
console.log(subject, detail.snippet);
|
|
719
|
-
}
|
|
626
|
+
// \u4EE3\u7406\u5BFE\u8C61\u30E6\u30FC\u30B6\u30FC\u306F Project Knowledge ("#### \u30B9\u30B3\u30FC\u30D7") \u304B\u3089\u9078\u3076
|
|
627
|
+
const subject = "alice@example.com";
|
|
628
|
+
const READ = ["https://www.googleapis.com/auth/gmail.readonly"];
|
|
720
629
|
|
|
721
|
-
|
|
722
|
-
|
|
630
|
+
const profileRes = await gmail.requestWithDelegation("/me/profile", {
|
|
631
|
+
subject,
|
|
632
|
+
scopes: READ,
|
|
633
|
+
});
|
|
634
|
+
const profile = await profileRes.json();
|
|
635
|
+
console.log(profile.emailAddress, profile.messagesTotal);
|
|
723
636
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
637
|
+
const messagesRes = await gmail.requestWithDelegation(
|
|
638
|
+
"/me/messages?maxResults=10",
|
|
639
|
+
{ subject, scopes: READ },
|
|
640
|
+
);
|
|
641
|
+
const messages = await messagesRes.json();
|
|
727
642
|
|
|
728
|
-
|
|
729
|
-
const
|
|
730
|
-
|
|
643
|
+
for (const msg of messages.messages ?? []) {
|
|
644
|
+
const detailRes = await gmail.requestWithDelegation(
|
|
645
|
+
\`/me/messages/\${msg.id}?format=metadata\`,
|
|
646
|
+
{ subject, scopes: READ },
|
|
647
|
+
);
|
|
648
|
+
const detail = await detailRes.json();
|
|
649
|
+
const subjectHeader = detail.payload.headers.find(h => h.name === "Subject")?.value;
|
|
650
|
+
console.log(subjectHeader, detail.snippet);
|
|
651
|
+
}
|
|
731
652
|
\`\`\``
|
|
732
653
|
},
|
|
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
|
-
}
|
|
654
|
+
tools
|
|
789
655
|
});
|
|
790
656
|
|
|
791
657
|
// src/connectors/create-connector-sdk.ts
|