@squadbase/vite-server 0.1.7-dev.7 → 0.1.8-dev.468a970

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.
@@ -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 used to authenticate with Google Cloud Platform. The service account must have domain-wide delegation enabled and be granted the Gmail API scope in Google Workspace admin.",
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
- let cachedToken = null;
128
- let tokenExpiresAt = 0;
129
- async function getAccessToken() {
130
- const nowSec = Math.floor(Date.now() / 1e3);
131
- if (cachedToken && nowSec < tokenExpiresAt - 60) {
132
- return cachedToken;
133
- }
134
- const jwt = buildJwt(
135
- serviceAccountKey.client_email,
136
- serviceAccountKey.private_key,
137
- delegatedUserEmail,
138
- nowSec
139
- );
140
- const response = await fetch(TOKEN_URL, {
141
- method: "POST",
142
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
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 (read-only, GET only)"),
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 requestTool = new ConnectorTool({
443
- name: "request",
444
- description: `Send authenticated GET requests to the Gmail API v1.
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 DwD 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
- scopes: ["https://www.googleapis.com/auth/gmail.readonly"],
470
- clientOptions: {
471
- subject: delegatedUserEmail
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 errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
500
- return { success: false, error: errorMessage };
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 { success: false, error: msg };
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 requestToolName = `gmail-service-account_${requestTool.name}`;
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: `\u4EE5\u4E0B\u306E\u624B\u9806\u3067Gmail\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\u3063\u3066\u304F\u3060\u3055\u3044\u3002\u63A5\u7D9A\u4F5C\u6210\u6642\u306B\u306F\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8JSON\u306E\u307F\u304C\u8A2D\u5B9A\u6E08\u307F\u3067\u3001\u59D4\u4EFB\u5BFE\u8C61\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306F\u3053\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u4E2D\u306B\u53D6\u5F97\u3057\u307E\u3059\u3002
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\u3001\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u304CDomain-wide Delegation\u3067\u4EE3\u7406\u30A2\u30AF\u30BB\u30B9\u3059\u308BGoogle Workspace\u30E6\u30FC\u30B6\u30FC\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u30D2\u30A2\u30EA\u30F3\u30B0\u3059\u308B:
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\u308BGoogle Workspace\u30E6\u30FC\u30B6\u30FC\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u300D
522
- - \`placeholder\`: \`"user@example.com"\`
523
- 2. \u53D7\u3051\u53D6\u3063\u305F\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092 \`updateConnectionParameters\` \u3067\u4FDD\u5B58\u3059\u308B:
524
- - \`parameterSlug\`: \`"delegated-user-email"\`
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
- 4. \u30A8\u30E9\u30FC\u304C\u8FD4\u3055\u308C\u305F\u5834\u5408\u3001\u4EE5\u4E0B\u3092\u78BA\u8A8D\u3059\u308B\u3088\u3046\u30E6\u30FC\u30B6\u30FC\u306B\u4F1D\u3048\u308B:
530
- - \u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u30C9\u30E1\u30A4\u30F3\u5168\u4F53\u306E\u59D4\u4EFB\u304C\u6709\u52B9\u304B
531
- - Google Workspace\u7BA1\u7406\u30B3\u30F3\u30BD\u30FC\u30EB\u3067Gmail API\u30B9\u30B3\u30FC\u30D7\u304C\u8A31\u53EF\u3055\u308C\u3066\u3044\u308B\u304B
532
- - \u5165\u529B\u3055\u308C\u305F\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u304C\u6B63\u3057\u3044\u304B
533
- 5. \`${requestToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u30E9\u30D9\u30EB\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B:
534
- - \`method\`: \`"GET"\`
535
- - \`path\`: \`"/me/labels"\`
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: "DwD \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
- - **\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\u5B9F\u884C\u3057\u3066\u3088\u3044\u306E\u306F\u4E0A\u8A18\u624B\u9806\u3067\u6307\u5B9A\u3055\u308C\u305F\u30D7\u30ED\u30D5\u30A3\u30FC\u30EB\u53D6\u5F97\u3068\u30E9\u30D9\u30EB\u4E00\u89A7\u53D6\u5F97\u306E\u307F
539
- - \u30C4\u30FC\u30EB\u9593\u306F1\u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057\u3002\u4E0D\u8981\u306A\u8AAC\u660E\u306F\u7701\u7565\u3057\u3001\u52B9\u7387\u7684\u306B\u9032\u3081\u308B`,
540
- en: `Follow these steps to set up the Gmail (Service Account) connection. Only the service account JSON is provided at connection creation time \u2014 the delegated user email is collected during this setup flow.
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 ask the user for the Google Workspace user email the service account will impersonate via Domain-wide Delegation:
417
+ 1. Call \`askUserQuestion\` to collect target emails:
543
418
  - \`type\`: \`"freeText"\`
544
- - \`question\`: "Please enter the email address of the Google Workspace user whose Gmail mailbox you want to access"
545
- - \`placeholder\`: \`"user@example.com"\`
546
- 2. Save the email via \`updateConnectionParameters\`:
547
- - \`parameterSlug\`: \`"delegated-user-email"\`
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
- 4. If an error is returned, ask the user to verify:
553
- - Domain-wide delegation is enabled for the service account
554
- - Gmail API scope is authorized in Google Workspace admin console
555
- - The entered email address is correct
556
- 5. Call \`${requestToolName}\` to get the label list:
557
- - \`method\`: \`"GET"\`
558
- - \`path\`: \`"/me/labels"\`
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 DwD \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
- - **Do NOT read message bodies during setup**. Only the profile and label list requests specified above are allowed
562
- - Write only 1 sentence between tool calls, then immediately call the next tool. Skip unnecessary explanations and proceed efficiently`
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: `1. Call gmail-service-account_request with GET /me/labels to list all labels
566
- 2. Call gmail-service-account_request with GET /me/messages?maxResults=5 to get recent message IDs
567
- 3. Call gmail-service-account_request with GET /me/messages/{id}?format=metadata for each message to see subjects and senders`,
568
- ja: `1. gmail-service-account_request \u3067 GET /me/labels \u3092\u547C\u3073\u51FA\u3057\u3001\u5168\u30E9\u30D9\u30EB\u4E00\u89A7\u3092\u53D6\u5F97
569
- 2. gmail-service-account_request \u3067 GET /me/messages?maxResults=5 \u3092\u547C\u3073\u51FA\u3057\u3001\u6700\u65B0\u30E1\u30C3\u30BB\u30FC\u30B8ID\u3092\u53D6\u5F97
570
- 3. \u5404\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u3064\u3044\u3066 gmail-service-account_request \u3067 GET /me/messages/{id}?format=metadata \u3092\u547C\u3073\u51FA\u3057\u3001\u4EF6\u540D\u3068\u9001\u4FE1\u8005\u3092\u78BA\u8A8D`
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 = { request: requestTool };
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. Read-only access to messages, threads, and labels.",
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-account_request\`: The only way to call the Gmail API (read-only). Use it to list messages, get message details, list labels, list threads, and get user profile. Authentication is handled automatically using a service account with domain-wide delegation.
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 delegated user's profile (email address, total messages/threads)
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 delegated user
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
- // Get user profile
637
- const profile = await gmail.getProfile();
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
- // List recent messages
641
- const messages = await gmail.listMessages({ maxResults: 10 });
642
- for (const msg of messages.messages) {
643
- const detail = await gmail.getMessage(msg.id, "metadata");
644
- const subject = detail.payload.headers.find(h => h.name === "Subject")?.value;
645
- console.log(subject, detail.snippet);
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
- // Search messages
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
- // List labels
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
- // Get a thread
656
- const thread = await gmail.getThread("<threadId>");
657
- thread.messages.forEach(m => console.log(m.snippet));
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
- - \`gmail-service-account_request\`: Gmail API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\uFF08\u8AAD\u307F\u53D6\u308A\u5C02\u7528\uFF09\u3002\u30E1\u30C3\u30BB\u30FC\u30B8\u4E00\u89A7\u306E\u53D6\u5F97\u3001\u30E1\u30C3\u30BB\u30FC\u30B8\u8A73\u7D30\u306E\u53D6\u5F97\u3001\u30E9\u30D9\u30EB\u4E00\u89A7\u3001\u30B9\u30EC\u30C3\u30C9\u4E00\u89A7\u3001\u30E6\u30FC\u30B6\u30FC\u30D7\u30ED\u30D5\u30A3\u30FC\u30EB\u306E\u53D6\u5F97\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u30C9\u30E1\u30A4\u30F3\u5168\u4F53\u306E\u59D4\u4EFB\u3092\u4F7F\u7528\u3057\u3066\u8A8D\u8A3C\u306F\u81EA\u52D5\u7684\u306B\u51E6\u7406\u3055\u308C\u307E\u3059\u3002
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 \u59D4\u4EFB\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
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 \u59D4\u4EFB\u30E6\u30FC\u30B6\u30FC\u3092\u6307\u3057\u307E\u3059
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 DwD \u3067 Workspace \u30E6\u30FC\u30B6\u30FC\u306B\u306A\u308A\u3059\u307E\u3057\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
- // Get user profile
710
- const profile = await gmail.getProfile();
711
- console.log(profile.emailAddress, profile.messagesTotal);
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
- // Search messages
722
- const results = await gmail.listMessages({ q: "from:boss@company.com is:unread" });
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
- // List labels
725
- const labels = await gmail.listLabels();
726
- labels.labels.forEach(l => console.log(l.name, l.messagesTotal));
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
- // Get a thread
729
- const thread = await gmail.getThread("<threadId>");
730
- thread.messages.forEach(m => console.log(m.snippet));
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