@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.
Files changed (88) hide show
  1. package/dist/cli/index.js +78505 -64782
  2. package/dist/cli/{sshcrypto-VVJOJ3LR.node → sshcrypto-P3UBA7BP.node} +0 -0
  3. package/dist/connectors/airtable-oauth.js +3 -0
  4. package/dist/connectors/airtable.js +3 -0
  5. package/dist/connectors/amplitude.js +3 -0
  6. package/dist/connectors/anthropic.js +3 -0
  7. package/dist/connectors/asana.js +18 -2
  8. package/dist/connectors/attio.js +3 -0
  9. package/dist/connectors/aws-billing.d.ts +5 -0
  10. package/dist/connectors/aws-billing.js +29843 -0
  11. package/dist/connectors/azure-sql.d.ts +5 -0
  12. package/dist/connectors/azure-sql.js +657 -0
  13. package/dist/connectors/backlog-api-key.js +3 -0
  14. package/dist/connectors/clickup.d.ts +5 -0
  15. package/dist/connectors/clickup.js +850 -0
  16. package/dist/connectors/customerio.js +3 -0
  17. package/dist/connectors/dbt.js +3 -0
  18. package/dist/connectors/freshdesk.d.ts +5 -0
  19. package/dist/connectors/freshdesk.js +842 -0
  20. package/dist/connectors/freshsales.d.ts +5 -0
  21. package/dist/connectors/freshsales.js +867 -0
  22. package/dist/connectors/freshservice.d.ts +5 -0
  23. package/dist/connectors/freshservice.js +813 -0
  24. package/dist/connectors/gamma.js +3 -0
  25. package/dist/connectors/gemini.js +3 -0
  26. package/dist/connectors/github.d.ts +5 -0
  27. package/dist/connectors/github.js +963 -0
  28. package/dist/connectors/gmail-oauth.js +18 -2
  29. package/dist/connectors/gmail.js +236 -352
  30. package/dist/connectors/google-ads.js +3 -0
  31. package/dist/connectors/google-analytics-oauth.js +3 -0
  32. package/dist/connectors/google-analytics.js +3 -0
  33. package/dist/connectors/google-audit-log.d.ts +5 -0
  34. package/dist/connectors/google-audit-log.js +813 -0
  35. package/dist/connectors/google-calendar-oauth.js +21 -2
  36. package/dist/connectors/google-calendar.js +548 -453
  37. package/dist/connectors/google-docs.js +21 -2
  38. package/dist/connectors/google-drive.js +18 -2
  39. package/dist/connectors/google-search-console-oauth.d.ts +5 -0
  40. package/dist/connectors/google-search-console-oauth.js +923 -0
  41. package/dist/connectors/google-sheets.js +21 -2
  42. package/dist/connectors/google-slides.js +21 -2
  43. package/dist/connectors/grafana.js +3 -0
  44. package/dist/connectors/hubspot-oauth.js +3 -0
  45. package/dist/connectors/hubspot.js +3 -0
  46. package/dist/connectors/influxdb.js +3 -0
  47. package/dist/connectors/intercom-oauth.js +3 -0
  48. package/dist/connectors/intercom.js +3 -0
  49. package/dist/connectors/jdbc.d.ts +5 -0
  50. package/dist/connectors/jdbc.js +21097 -0
  51. package/dist/connectors/jira-api-key.js +3 -0
  52. package/dist/connectors/kintone-api-token.js +3 -0
  53. package/dist/connectors/kintone.js +3 -0
  54. package/dist/connectors/linear.js +3 -0
  55. package/dist/connectors/linkedin-ads.js +3 -0
  56. package/dist/connectors/mailchimp-oauth.js +3 -0
  57. package/dist/connectors/mailchimp.js +3 -0
  58. package/dist/connectors/meta-ads-oauth.js +3 -0
  59. package/dist/connectors/meta-ads.js +3 -0
  60. package/dist/connectors/mixpanel.js +3 -0
  61. package/dist/connectors/monday.d.ts +5 -0
  62. package/dist/connectors/monday.js +813 -0
  63. package/dist/connectors/notion-oauth.js +3 -0
  64. package/dist/connectors/notion.js +3 -0
  65. package/dist/connectors/openai.js +3 -0
  66. package/dist/connectors/oracle.d.ts +5 -0
  67. package/dist/connectors/oracle.js +665 -0
  68. package/dist/connectors/salesforce.js +3 -0
  69. package/dist/connectors/semrush.d.ts +5 -0
  70. package/dist/connectors/semrush.js +812 -0
  71. package/dist/connectors/sentry.js +3 -0
  72. package/dist/connectors/shopify-oauth.js +3 -0
  73. package/dist/connectors/shopify.js +3 -0
  74. package/dist/connectors/sqlserver.d.ts +5 -0
  75. package/dist/connectors/sqlserver.js +656 -0
  76. package/dist/connectors/stripe-api-key.js +3 -0
  77. package/dist/connectors/stripe-oauth.js +3 -0
  78. package/dist/connectors/supabase.d.ts +5 -0
  79. package/dist/connectors/supabase.js +582 -0
  80. package/dist/connectors/tiktok-ads.js +18 -2
  81. package/dist/connectors/wix-store.js +3 -0
  82. package/dist/connectors/zendesk-oauth.js +3 -0
  83. package/dist/connectors/zendesk.js +3 -0
  84. package/dist/index.js +71474 -57751
  85. package/dist/main.js +78053 -64330
  86. package/dist/{sshcrypto-VVJOJ3LR.node → sshcrypto-P3UBA7BP.node} +0 -0
  87. package/dist/vite-plugin.js +77960 -64237
  88. package/package.json +64 -2
@@ -42,82 +42,28 @@ var ParameterDefinition = class {
42
42
  }
43
43
  };
44
44
 
45
- // ../connectors/src/connectors/google-calendar/sdk/index.ts
46
- import * as crypto from "crypto";
47
-
48
45
  // ../connectors/src/connectors/google-calendar/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. Ensure that the service account has the necessary permissions to access Google Calendar, and that calendars are shared with the service account email.",
50
+ description: "The service account JSON key. Used for both Domain-wide Delegation (impersonating a Workspace user) and direct service-account access (calendars explicitly shared with the SA email). The authentication path is selected per call by the tool used.",
54
51
  envVarBaseKey: "GOOGLE_CALENDAR_SERVICE_ACCOUNT_JSON_BASE64",
55
52
  type: "base64EncodedJson",
56
53
  secret: true,
57
54
  required: true
58
55
  })
59
56
  };
60
- var impersonateEmailParameter = new ParameterDefinition({
61
- slug: "impersonate-email",
62
- name: "User Email Address(es)",
63
- description: "The email address(es) of the Google Workspace user(s) whose calendar is accessed via Domain-wide Delegation. Collected during the setup flow.",
64
- envVarBaseKey: "GOOGLE_CALENDAR_IMPERSONATE_EMAIL",
65
- type: "text",
66
- secret: false,
67
- required: false
68
- });
69
- var calendarIdParameter = new ParameterDefinition({
70
- slug: "calendar-id",
71
- name: "Default Calendar ID",
72
- description: "The default Google Calendar ID to use (e.g., 'primary' or an email address like 'user@example.com'). If not set, 'primary' is used.",
73
- envVarBaseKey: "GOOGLE_CALENDAR_CALENDAR_ID",
74
- type: "text",
75
- secret: false,
76
- required: false
77
- });
78
57
 
79
58
  // ../connectors/src/connectors/google-calendar/sdk/index.ts
80
- var TOKEN_URL = "https://oauth2.googleapis.com/token";
81
59
  var BASE_URL = "https://www.googleapis.com/calendar/v3";
82
- var SCOPE = "https://www.googleapis.com/auth/calendar.readonly https://www.googleapis.com/auth/calendar.events.readonly";
83
- function base64url(input) {
84
- const buf = typeof input === "string" ? Buffer.from(input) : input;
85
- return buf.toString("base64url");
86
- }
87
- function buildJwt(clientEmail, privateKey, nowSec, subject) {
88
- const header = base64url(JSON.stringify({ alg: "RS256", typ: "JWT" }));
89
- const claims = {
90
- iss: clientEmail,
91
- scope: SCOPE,
92
- aud: TOKEN_URL,
93
- iat: nowSec,
94
- exp: nowSec + 3600
95
- };
96
- if (subject) {
97
- claims.sub = subject;
98
- }
99
- const payload = base64url(JSON.stringify(claims));
100
- const signingInput = `${header}.${payload}`;
101
- const sign = crypto.createSign("RSA-SHA256");
102
- sign.update(signingInput);
103
- sign.end();
104
- const signature = base64url(sign.sign(privateKey));
105
- return `${signingInput}.${signature}`;
106
- }
107
60
  function createClient(params) {
108
61
  const serviceAccountKeyJsonBase64 = params[parameters.serviceAccountKeyJsonBase64.slug];
109
- const impersonateEmail = params[impersonateEmailParameter.slug];
110
- const defaultCalendarId = params[calendarIdParameter.slug] ?? "primary";
111
62
  if (!serviceAccountKeyJsonBase64) {
112
63
  throw new Error(
113
64
  `google-calendar: missing required parameter: ${parameters.serviceAccountKeyJsonBase64.slug}`
114
65
  );
115
66
  }
116
- if (!impersonateEmail) {
117
- throw new Error(
118
- `google-calendar: missing required parameter: ${impersonateEmailParameter.slug}`
119
- );
120
- }
121
67
  let serviceAccountKey;
122
68
  try {
123
69
  const decoded = Buffer.from(
@@ -135,101 +81,48 @@ function createClient(params) {
135
81
  "google-calendar: service account key JSON must contain client_email and private_key"
136
82
  );
137
83
  }
138
- const subject = impersonateEmail;
139
- let cachedToken = null;
140
- let tokenExpiresAt = 0;
141
- async function getAccessToken2() {
142
- const nowSec = Math.floor(Date.now() / 1e3);
143
- if (cachedToken && nowSec < tokenExpiresAt - 60) {
144
- return cachedToken;
145
- }
146
- const jwt = buildJwt(
147
- serviceAccountKey.client_email,
148
- serviceAccountKey.private_key,
149
- nowSec,
150
- subject
151
- );
152
- const response = await fetch(TOKEN_URL, {
153
- method: "POST",
154
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
155
- body: new URLSearchParams({
156
- grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
157
- assertion: jwt
158
- })
159
- });
160
- if (!response.ok) {
161
- const text = await response.text();
162
- throw new Error(
163
- `google-calendar: token exchange failed (${response.status}): ${text}`
164
- );
165
- }
166
- const data = await response.json();
167
- cachedToken = data.access_token;
168
- tokenExpiresAt = nowSec + data.expires_in;
169
- return cachedToken;
170
- }
171
- function resolveCalendarId(override) {
172
- return override ?? defaultCalendarId;
84
+ function buildUrl(path2) {
85
+ return `${BASE_URL}${path2.startsWith("/") ? "" : "/"}${path2}`;
173
86
  }
174
87
  return {
175
- async request(path2, init) {
176
- const accessToken = await getAccessToken2();
177
- const resolvedPath = path2.replace(
178
- /\{calendarId\}/g,
179
- defaultCalendarId
180
- );
181
- const url = `${BASE_URL}${resolvedPath.startsWith("/") ? "" : "/"}${resolvedPath}`;
182
- const headers = new Headers(init?.headers);
183
- headers.set("Authorization", `Bearer ${accessToken}`);
184
- return fetch(url, { ...init, headers });
185
- },
186
- async listCalendars() {
187
- const response = await this.request("/users/me/calendarList", {
188
- method: "GET"
88
+ async requestWithDelegation(path2, { subject, scopes, init }) {
89
+ const { GoogleAuth } = await import("google-auth-library");
90
+ const auth = new GoogleAuth({
91
+ credentials: {
92
+ client_email: serviceAccountKey.client_email,
93
+ private_key: serviceAccountKey.private_key
94
+ },
95
+ scopes,
96
+ clientOptions: { subject }
189
97
  });
190
- if (!response.ok) {
191
- const text = await response.text();
192
- throw new Error(
193
- `google-calendar: listCalendars failed (${response.status}): ${text}`
194
- );
195
- }
196
- const data = await response.json();
197
- return data.items ?? [];
198
- },
199
- async listEvents(options, calendarId) {
200
- const cid = resolveCalendarId(calendarId);
201
- const searchParams = new URLSearchParams();
202
- if (options?.timeMin) searchParams.set("timeMin", options.timeMin);
203
- if (options?.timeMax) searchParams.set("timeMax", options.timeMax);
204
- if (options?.maxResults)
205
- searchParams.set("maxResults", String(options.maxResults));
206
- if (options?.q) searchParams.set("q", options.q);
207
- if (options?.singleEvents != null)
208
- searchParams.set("singleEvents", String(options.singleEvents));
209
- if (options?.orderBy) searchParams.set("orderBy", options.orderBy);
210
- if (options?.pageToken) searchParams.set("pageToken", options.pageToken);
211
- const qs = searchParams.toString();
212
- const path2 = `/calendars/${encodeURIComponent(cid)}/events${qs ? `?${qs}` : ""}`;
213
- const response = await this.request(path2, { method: "GET" });
214
- if (!response.ok) {
215
- const text = await response.text();
98
+ const token = await auth.getAccessToken();
99
+ if (!token) {
216
100
  throw new Error(
217
- `google-calendar: listEvents failed (${response.status}): ${text}`
101
+ `google-calendar: failed to obtain access token (subject=${subject})`
218
102
  );
219
103
  }
220
- return await response.json();
104
+ const headers = new Headers(init?.headers);
105
+ headers.set("Authorization", `Bearer ${token}`);
106
+ return fetch(buildUrl(path2), { ...init, headers });
221
107
  },
222
- async getEvent(eventId, calendarId) {
223
- const cid = resolveCalendarId(calendarId);
224
- const path2 = `/calendars/${encodeURIComponent(cid)}/events/${encodeURIComponent(eventId)}`;
225
- const response = await this.request(path2, { method: "GET" });
226
- if (!response.ok) {
227
- const text = await response.text();
108
+ async request(path2, { scopes, init }) {
109
+ const { GoogleAuth } = await import("google-auth-library");
110
+ const auth = new GoogleAuth({
111
+ credentials: {
112
+ client_email: serviceAccountKey.client_email,
113
+ private_key: serviceAccountKey.private_key
114
+ },
115
+ scopes
116
+ });
117
+ const token = await auth.getAccessToken();
118
+ if (!token) {
228
119
  throw new Error(
229
- `google-calendar: getEvent failed (${response.status}): ${text}`
120
+ "google-calendar: failed to obtain access token (no subject)"
230
121
  );
231
122
  }
232
- return await response.json();
123
+ const headers = new Headers(init?.headers);
124
+ headers.set("Authorization", `Bearer ${token}`);
125
+ return fetch(buildUrl(path2), { ...init, headers });
233
126
  }
234
127
  };
235
128
  }
@@ -287,6 +180,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
287
180
  releaseFlag;
288
181
  proxyPolicy;
289
182
  experimentalAttributes;
183
+ categories;
290
184
  onboarding;
291
185
  systemPrompt;
292
186
  tools;
@@ -302,6 +196,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
302
196
  this.releaseFlag = config.releaseFlag;
303
197
  this.proxyPolicy = config.proxyPolicy;
304
198
  this.experimentalAttributes = config.experimentalAttributes;
199
+ this.categories = config.categories ?? [];
305
200
  this.onboarding = config.onboarding;
306
201
  this.systemPrompt = config.systemPrompt;
307
202
  this.tools = config.tools;
@@ -381,97 +276,62 @@ var AUTH_TYPES = {
381
276
  USER_PASSWORD: "user-password"
382
277
  };
383
278
 
384
- // ../connectors/src/connectors/google-calendar/tools/list-calendars.ts
385
- import * as crypto2 from "crypto";
279
+ // ../connectors/src/connectors/google-calendar/tools/request.ts
386
280
  import { z } from "zod";
387
- var TOKEN_URL2 = "https://oauth2.googleapis.com/token";
388
- var BASE_URL2 = "https://www.googleapis.com/calendar/v3";
389
- var SCOPE2 = "https://www.googleapis.com/auth/calendar.readonly https://www.googleapis.com/auth/calendar.events.readonly";
390
- var REQUEST_TIMEOUT_MS = 6e4;
391
- function base64url2(input) {
392
- const buf = typeof input === "string" ? Buffer.from(input) : input;
393
- return buf.toString("base64url");
394
- }
395
- function buildJwt2(clientEmail, privateKey, nowSec, subject) {
396
- const header = base64url2(JSON.stringify({ alg: "RS256", typ: "JWT" }));
397
- const payload = base64url2(
398
- JSON.stringify({
399
- iss: clientEmail,
400
- sub: subject,
401
- scope: SCOPE2,
402
- aud: TOKEN_URL2,
403
- iat: nowSec,
404
- exp: nowSec + 3600
405
- })
406
- );
407
- const signingInput = `${header}.${payload}`;
408
- const sign = crypto2.createSign("RSA-SHA256");
409
- sign.update(signingInput);
410
- sign.end();
411
- const signature = base64url2(sign.sign(privateKey));
412
- return `${signingInput}.${signature}`;
413
- }
414
- async function getAccessToken(serviceAccount, subject) {
415
- const nowSec = Math.floor(Date.now() / 1e3);
416
- const jwt = buildJwt2(
417
- serviceAccount.client_email,
418
- serviceAccount.private_key,
419
- nowSec,
420
- subject
421
- );
422
- const response = await fetch(TOKEN_URL2, {
423
- method: "POST",
424
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
425
- body: new URLSearchParams({
426
- grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
427
- assertion: jwt
428
- })
429
- });
430
- if (!response.ok) {
431
- const text = await response.text();
432
- throw new Error(
433
- `token exchange failed for ${subject} (${response.status}): ${text}`
434
- );
281
+
282
+ // ../connectors/src/lib/normalize-path.ts
283
+ function normalizeRequestPath(path2, basePathSegment) {
284
+ let p = path2.trim();
285
+ if (!p.startsWith("/")) p = "/" + p;
286
+ if (p === basePathSegment || p.startsWith(basePathSegment + "/")) {
287
+ p = p.slice(basePathSegment.length) || "/";
435
288
  }
436
- const data = await response.json();
437
- return data.access_token;
289
+ return p;
290
+ }
291
+
292
+ // ../connectors/src/connectors/google-calendar/tools/request.ts
293
+ var BASE_HOST = "https://www.googleapis.com";
294
+ var BASE_PATH_SEGMENT = "/calendar/v3";
295
+ var BASE_URL2 = `${BASE_HOST}${BASE_PATH_SEGMENT}`;
296
+ var REQUEST_TIMEOUT_MS = 6e4;
297
+ function decodeServiceAccount(keyJsonBase64) {
298
+ const decoded = Buffer.from(keyJsonBase64, "base64").toString("utf-8");
299
+ return JSON.parse(decoded);
438
300
  }
439
301
  var inputSchema = z.object({
440
302
  toolUseIntent: z.string().optional().describe(
441
303
  "Brief description of what you intend to accomplish with this tool call"
442
304
  ),
443
- connectionId: z.string().describe("ID of the Google Calendar connection to use")
305
+ connectionId: z.string().describe("ID of the Google Calendar connection to use"),
306
+ method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).describe("HTTP method"),
307
+ path: z.string().describe(
308
+ "API path appended to https://www.googleapis.com/calendar/v3 (e.g., '/users/me/calendarList', '/calendars/team@example.com/events'). Write the calendar ID directly into the path \u2014 there is no placeholder substitution."
309
+ ),
310
+ scopes: z.array(z.string()).describe(
311
+ "OAuth scopes the token must include. This connector currently supports read-only operations only \u2014 pass one of ['https://www.googleapis.com/auth/calendar.readonly'] (calendars + events read), ['https://www.googleapis.com/auth/calendar.events.readonly'] (events read only), or ['https://www.googleapis.com/auth/calendar.freebusy'] (busy/free queries only). Per-endpoint scope reference: https://developers.google.com/calendar/api/auth"
312
+ ),
313
+ queryParams: z.record(z.string(), z.string()).optional().describe("Query parameters to append to the URL"),
314
+ body: z.record(z.string(), z.unknown()).optional().describe("JSON request body for POST/PUT/PATCH")
444
315
  });
445
316
  var outputSchema = z.discriminatedUnion("success", [
446
317
  z.object({
447
318
  success: z.literal(true),
448
- calendars: z.array(
449
- z.object({
450
- impersonateEmail: z.string(),
451
- id: z.string(),
452
- summary: z.string(),
453
- primary: z.boolean().optional(),
454
- accessRole: z.string()
455
- })
456
- ),
457
- errors: z.array(
458
- z.object({
459
- impersonateEmail: z.string(),
460
- error: z.string()
461
- })
462
- )
319
+ status: z.number(),
320
+ data: z.record(z.string(), z.unknown()),
321
+ serviceAccountEmail: z.string()
463
322
  }),
464
323
  z.object({
465
324
  success: z.literal(false),
466
- error: z.string()
325
+ error: z.string(),
326
+ serviceAccountEmail: z.string().optional()
467
327
  })
468
328
  ]);
469
- var listCalendarsTool = new ConnectorTool({
470
- name: "listCalendars",
471
- description: "List Google Calendars accessible via Domain-wide Delegation by impersonating the Google Workspace user(s) configured on the connection's `impersonate-email` parameter (comma-separated list supported). Use during setup to aggregate calendars across the configured emails.",
329
+ var requestTool = new ConnectorTool({
330
+ name: "request",
331
+ description: "Call the Google Calendar API as the service account itself (no delegation). Read-only operations only. Only calendars explicitly shared with the service account email are accessible. Pass `scopes` as a read-only Calendar scope (e.g., ['https://www.googleapis.com/auth/calendar.readonly']). Use this tool when the project knowledge records the calendar with `(service-account, ...)` (no `subject`).",
472
332
  inputSchema,
473
333
  outputSchema,
474
- async execute({ connectionId }, connections) {
334
+ async execute({ connectionId, method, path: path2, scopes, queryParams, body }, connections) {
475
335
  const connection2 = connections.find((c) => c.id === connectionId);
476
336
  if (!connection2) {
477
337
  return {
@@ -479,144 +339,97 @@ var listCalendarsTool = new ConnectorTool({
479
339
  error: `Connection ${connectionId} not found`
480
340
  };
481
341
  }
482
- const impersonateEmailRaw = impersonateEmailParameter.getValue(connection2);
483
- const emails = impersonateEmailRaw.split(",").map((e) => e.trim()).filter((e) => e.length > 0);
484
- if (emails.length === 0) {
485
- return {
486
- success: false,
487
- error: "impersonate-email parameter is empty"
488
- };
489
- }
490
- console.log(
491
- `[connector-request] google-calendar/${connection2.name}: listCalendars for ${emails.join(",")}`
492
- );
342
+ const keyJsonBase64 = parameters.serviceAccountKeyJsonBase64.getValue(connection2);
493
343
  let serviceAccount;
494
344
  try {
495
- const keyJsonBase64 = parameters.serviceAccountKeyJsonBase64.getValue(connection2);
496
- const decoded = Buffer.from(keyJsonBase64, "base64").toString("utf-8");
497
- serviceAccount = JSON.parse(decoded);
345
+ serviceAccount = decodeServiceAccount(keyJsonBase64);
498
346
  } catch (err) {
499
347
  const msg = err instanceof Error ? err.message : String(err);
500
348
  return {
501
349
  success: false,
502
- error: `failed to decode service account key: ${msg}`
503
- };
504
- }
505
- if (!serviceAccount.client_email || !serviceAccount.private_key) {
506
- return {
507
- success: false,
508
- error: "service account key JSON must contain client_email and private_key"
350
+ error: `Failed to decode service account key: ${msg}`
509
351
  };
510
352
  }
511
- const aggregated = [];
512
- const errors = [];
513
- for (const email of emails) {
353
+ const serviceAccountEmail = serviceAccount.client_email;
354
+ console.log(
355
+ `[connector-request] google-calendar/${connection2.name}: ${method} ${path2} (service account)`
356
+ );
357
+ try {
358
+ const { GoogleAuth } = await import("google-auth-library");
359
+ const auth = new GoogleAuth({
360
+ credentials: {
361
+ client_email: serviceAccount.client_email,
362
+ private_key: serviceAccount.private_key
363
+ },
364
+ scopes
365
+ });
366
+ const token = await auth.getAccessToken();
367
+ if (!token) {
368
+ return {
369
+ success: false,
370
+ error: "Failed to obtain access token",
371
+ serviceAccountEmail
372
+ };
373
+ }
374
+ const normalizedPath = normalizeRequestPath(path2, BASE_PATH_SEGMENT);
375
+ let url = `${BASE_URL2}${normalizedPath}`;
376
+ if (queryParams) {
377
+ const searchParams = new URLSearchParams(queryParams);
378
+ url += `?${searchParams.toString()}`;
379
+ }
514
380
  const controller = new AbortController();
515
381
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
516
382
  try {
517
- const token = await getAccessToken(serviceAccount, email);
518
- const response = await fetch(`${BASE_URL2}/users/me/calendarList`, {
519
- method: "GET",
520
- headers: { Authorization: `Bearer ${token}` },
383
+ const hasBody = body != null && ["POST", "PUT", "PATCH"].includes(method);
384
+ const response = await fetch(url, {
385
+ method,
386
+ headers: {
387
+ Authorization: `Bearer ${token}`,
388
+ "Content-Type": "application/json"
389
+ },
390
+ body: hasBody ? JSON.stringify(body) : void 0,
521
391
  signal: controller.signal
522
392
  });
523
- const data = await response.json();
393
+ const data = await response.json().catch(() => ({}));
524
394
  if (!response.ok) {
525
395
  const errorObj = data?.error;
526
- errors.push({
527
- impersonateEmail: email,
528
- error: errorObj?.message ?? `HTTP ${response.status} ${response.statusText}`
529
- });
530
- continue;
531
- }
532
- const items = data.items ?? [];
533
- for (const c of items) {
534
- aggregated.push({
535
- impersonateEmail: email,
536
- id: c.id,
537
- summary: c.summary,
538
- primary: c.primary,
539
- accessRole: c.accessRole
540
- });
396
+ const errorMessage = errorObj?.message ?? (typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`);
397
+ return {
398
+ success: false,
399
+ error: errorMessage,
400
+ serviceAccountEmail
401
+ };
541
402
  }
542
- } catch (err) {
543
- const msg = err instanceof Error ? err.message : String(err);
544
- errors.push({ impersonateEmail: email, error: msg });
403
+ return {
404
+ success: true,
405
+ status: response.status,
406
+ data,
407
+ serviceAccountEmail
408
+ };
545
409
  } finally {
546
410
  clearTimeout(timeout);
547
411
  }
412
+ } catch (err) {
413
+ const msg = err instanceof Error ? err.message : String(err);
414
+ return {
415
+ success: false,
416
+ error: msg,
417
+ serviceAccountEmail
418
+ };
548
419
  }
549
- return {
550
- success: true,
551
- calendars: aggregated,
552
- errors
553
- };
554
420
  }
555
421
  });
556
422
 
557
- // ../connectors/src/connectors/google-calendar/setup.ts
558
- var listCalendarsToolName = `google-calendar-service-account_${listCalendarsTool.name}`;
559
- var googleCalendarOnboarding = new ConnectorOnboarding({
560
- connectionSetupInstructions: {
561
- ja: `\u4EE5\u4E0B\u306E\u624B\u9806\u3067Google Calendar\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\u5BFE\u8C61\u30E6\u30FC\u30B6\u30FC\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3084\u30AB\u30EC\u30F3\u30C0\u30FCID\u306F\u3053\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u4E2D\u306B\u53D6\u5F97\u3057\u307E\u3059\u3002
562
-
563
- 1. \`askUserQuestion\` \u3067\u30E6\u30FC\u30B6\u30FC\u306B\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:
564
- - \`type\`: \`"freeText"\`
565
- - \`question\`: \u300C\u30A2\u30AF\u30BB\u30B9\u3057\u305F\u3044\u30AB\u30EC\u30F3\u30C0\u30FC\u3092\u6301\u3064\u30E6\u30FC\u30B6\u30FC\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u3042\u308B\u5834\u5408\u306F\u30AB\u30F3\u30DE\u533A\u5207\u308A\u3067\u5165\u529B\u53EF\uFF09\u300D
566
- - \`placeholder\`: \`"user@example.com, admin@example.com"\`
567
- 2. \u30E6\u30FC\u30B6\u30FC\u304B\u3089\u53D7\u3051\u53D6\u3063\u305F\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\uFF08\u30AB\u30F3\u30DE\u533A\u5207\u308A\u5BFE\u5FDC\uFF09\u3092 \`updateConnectionParameters\` \u3067\u4FDD\u5B58\u3059\u308B:
568
- - \`parameterSlug\`: \`"impersonate-email"\`
569
- - \`options\`: \`[{ value: <\u5165\u529B\u3055\u308C\u305F\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u6587\u5B57\u5217>, label: <\u540C\u3058\u5024> }]\`\uFF081\u4EF6\u306E\u307F\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u306F\u81EA\u52D5\u9078\u629E\u3055\u308C\u308B\uFF09
570
- 3. \`${listCalendarsToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3001\u4FDD\u5B58\u3057\u305F\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u30AB\u30EC\u30F3\u30C0\u30FC\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B\u3002
571
- - \`errors\` \u306B\u30A8\u30E9\u30FC\u304C\u3042\u308A \`calendars\` \u304C\u7A7A\u306E\u5834\u5408\uFF08\u3059\u3079\u3066\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3067\u5931\u6557\uFF09\u3001\u5165\u529B\u3055\u308C\u305F\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u304C\u5B58\u5728\u3057\u306A\u3044\u53EF\u80FD\u6027\u304C\u3042\u308B\u3002\`askUserQuestion\` \u3067\u300C{\u5165\u529B\u3057\u305F\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9} \u306E\u30AB\u30EC\u30F3\u30C0\u30FC\u306B\u30A2\u30AF\u30BB\u30B9\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u30A2\u30C9\u30EC\u30B9\u306B\u8AA4\u308A\u304C\u3042\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002\u4F3C\u305F\u30A2\u30C9\u30EC\u30B9\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u304B\uFF1F\u300D\u3068\u805E\u304D\u8FD4\u3057\u3001\u30B9\u30C6\u30C3\u30D72\u304B\u3089\u518D\u5EA6\u5B9F\u884C\u3059\u308B
572
- 4. \u8FD4\u5374\u3055\u308C\u305F \`calendars\` \u914D\u5217\uFF08\u5404\u8981\u7D20: \`{ impersonateEmail, id, summary, primary, accessRole }\`\uFF09\u3092\u5143\u306B\u300C\u4F7F\u7528\u3059\u308B\u30AB\u30EC\u30F3\u30C0\u30FC\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u300D\u3068\u77ED\u304F\u4F1D\u3048\u305F\u4E0A\u3067\u3001\`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
573
- - \`parameterSlug\`: \`"calendar-id"\`
574
- - \`options\`: \u5404 option \u306E \`label\` \u306F \`\u30AB\u30EC\u30F3\u30C0\u30FC\u540D (owner: impersonateEmail)\` \u306E\u5F62\u5F0F\u3001\`value\` \u306F\u30AB\u30EC\u30F3\u30C0\u30FCID
575
- - \`errors\` \u306B\u5931\u6557\u3057\u305F\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u304C\u3042\u308B\u5834\u5408\u306F\u3001\u305D\u306E\u65E8\u3092\u77ED\u304F\u4F1D\u3048\u308B
576
- 5. \u30E6\u30FC\u30B6\u30FC\u304C\u9078\u629E\u3057\u305F\u30AB\u30EC\u30F3\u30C0\u30FC\u306E \`label\` \u304B\u3089 owner \u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u62BD\u51FA\u3057\u3001\`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3057\u3066 \`impersonate-email\` \u3092\u6700\u7D42\u5024\u3067\u4E0A\u66F8\u304D\u3059\u308B:
577
- - \`parameterSlug\`: \`"impersonate-email"\`
578
- - \`options\`: \`[{ value: <ownerEmail>, label: <ownerEmail> }]\`
579
-
580
- #### \u5236\u7D04
581
- - **\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u30C9\u30E1\u30A4\u30F3\u5168\u4F53\u306E\u59D4\u4EFB\u8A2D\u5B9A\u304C\u5FC5\u8981\u3067\u3059**\u3002\`${listCalendarsToolName}\` \u306E \`errors\` \u306B\u6A29\u9650\u30A8\u30E9\u30FC\u304C\u51FA\u308B\u5834\u5408\u3001Google Workspace\u7BA1\u7406\u8005\u306BDomain-wide Delegation\u306E\u8A2D\u5B9A\u78BA\u8A8D\u3092\u4FC3\u3057\u3066\u304F\u3060\u3055\u3044
582
- - \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`,
583
- en: `Follow these steps to set up the Google Calendar connection. Only the service account JSON is provided at connection creation time \u2014 the target user email and calendar ID are collected during this setup flow.
584
-
585
- 1. Call \`askUserQuestion\` to ask the user for the Google Workspace user email the service account will impersonate via Domain-wide Delegation:
586
- - \`type\`: \`"freeText"\`
587
- - \`question\`: "Please enter the email address of the user whose calendar you want to access (comma-separated list allowed for multiple users)"
588
- - \`placeholder\`: \`"user@example.com, admin@example.com"\`
589
- 2. Save the email(s) the user provided (comma-separated supported) via \`updateConnectionParameters\`:
590
- - \`parameterSlug\`: \`"impersonate-email"\`
591
- - \`options\`: \`[{ value: <the email string entered>, label: <same value> }]\` (a single option is auto-selected)
592
- 3. Call \`${listCalendarsToolName}\` to list calendars accessible via the saved email(s).
593
- - If \`errors\` is non-empty and \`calendars\` is empty (all emails failed), the entered address may not exist. Use \`askUserQuestion\` to ask: "Could not access the calendar for {entered email}. The address may be incorrect \u2014 did you mean a similar address?" Then re-run from step 2 with the new input
594
- 4. Using the returned \`calendars\` array (each item: \`{ impersonateEmail, id, summary, primary, accessRole }\`), briefly say "Please select a calendar." then call \`updateConnectionParameters\`:
595
- - \`parameterSlug\`: \`"calendar-id"\`
596
- - \`options\`: Each option's \`label\` should be \`Calendar Name (owner: impersonateEmail)\`, \`value\` should be the calendar ID
597
- - If \`errors\` contains failing email addresses, briefly mention them
598
- 5. Extract the owner email from the \`label\` of the user's selected calendar, then call \`updateConnectionParameters\` to overwrite \`impersonate-email\` with the final value:
599
- - \`parameterSlug\`: \`"impersonate-email"\`
600
- - \`options\`: \`[{ value: <ownerEmail>, label: <ownerEmail> }]\`
601
-
602
- #### Constraints
603
- - **Domain-wide Delegation must be configured on the service account**. If \`${listCalendarsToolName}\` returns permission errors in the \`errors\` field, ask the user to verify the Domain-wide Delegation setup with their Google Workspace administrator
604
- - Write only 1 sentence between tool calls, then immediately call the next tool. Skip unnecessary explanations and proceed efficiently`
605
- },
606
- dataOverviewInstructions: {
607
- en: `1. Call google-calendar-service-account_request with GET /calendars/{calendarId} to get the default calendar's metadata
608
- 2. Call google-calendar-service-account_request with GET /users/me/calendarList to list all accessible calendars
609
- 3. Call google-calendar-service-account_request with GET /calendars/{calendarId}/events with query params timeMin (RFC3339) and maxResults=10 to sample upcoming events`,
610
- ja: `1. google-calendar-service-account_request \u3067 GET /calendars/{calendarId} \u3092\u547C\u3073\u51FA\u3057\u3001\u30C7\u30D5\u30A9\u30EB\u30C8\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u53D6\u5F97
611
- 2. google-calendar-service-account_request \u3067 GET /users/me/calendarList \u3092\u547C\u3073\u51FA\u3057\u3001\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u5168\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u4E00\u89A7\u3092\u53D6\u5F97
612
- 3. google-calendar-service-account_request \u3067 GET /calendars/{calendarId}/events \u3092\u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF timeMin\uFF08RFC3339\u5F62\u5F0F\uFF09\u3068 maxResults=10 \u3067\u547C\u3073\u51FA\u3057\u3001\u76F4\u8FD1\u306E\u30A4\u30D9\u30F3\u30C8\u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0`
613
- }
614
- });
615
-
616
- // ../connectors/src/connectors/google-calendar/tools/request.ts
423
+ // ../connectors/src/connectors/google-calendar/tools/request-with-delegation.ts
617
424
  import { z as z2 } from "zod";
618
- var BASE_URL3 = "https://www.googleapis.com/calendar/v3";
425
+ var BASE_HOST2 = "https://www.googleapis.com";
426
+ var BASE_PATH_SEGMENT2 = "/calendar/v3";
427
+ var BASE_URL3 = `${BASE_HOST2}${BASE_PATH_SEGMENT2}`;
619
428
  var REQUEST_TIMEOUT_MS2 = 6e4;
429
+ function decodeServiceAccount2(keyJsonBase64) {
430
+ const decoded = Buffer.from(keyJsonBase64, "base64").toString("utf-8");
431
+ return JSON.parse(decoded);
432
+ }
620
433
  var inputSchema2 = z2.object({
621
434
  toolUseIntent: z2.string().optional().describe(
622
435
  "Brief description of what you intend to accomplish with this tool call"
@@ -624,33 +437,38 @@ var inputSchema2 = z2.object({
624
437
  connectionId: z2.string().describe("ID of the Google Calendar connection to use"),
625
438
  method: z2.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).describe("HTTP method"),
626
439
  path: z2.string().describe(
627
- "API path appended to https://www.googleapis.com/calendar/v3 (e.g., '/calendars/{calendarId}/events'). {calendarId} is automatically replaced."
440
+ "API path appended to https://www.googleapis.com/calendar/v3 (e.g., '/users/me/calendarList', '/calendars/alice@example.com/events'). Write the calendar ID directly into the path \u2014 there is no placeholder substitution."
628
441
  ),
629
- queryParams: z2.record(z2.string(), z2.string()).optional().describe("Query parameters to append to the URL"),
630
- body: z2.record(z2.string(), z2.unknown()).optional().describe("Request body (JSON) for POST/PUT/PATCH methods"),
631
- subject: z2.string().optional().describe(
632
- "Override the email address of the user to impersonate via Domain-wide Delegation. If omitted, the connection's configured user email is used."
633
- )
442
+ subject: z2.string().describe(
443
+ "Email of the Workspace user to impersonate via Domain-wide Delegation. The token will be issued as this user."
444
+ ),
445
+ scopes: z2.array(z2.string()).describe(
446
+ "OAuth scopes the token must include. This connector currently supports read-only operations only \u2014 pass one of ['https://www.googleapis.com/auth/calendar.readonly'] (calendars + events read), ['https://www.googleapis.com/auth/calendar.events.readonly'] (events read only), or ['https://www.googleapis.com/auth/calendar.freebusy'] (busy/free queries only). Per-endpoint scope reference: https://developers.google.com/calendar/api/auth"
447
+ ),
448
+ queryParams: z2.record(z2.string(), z2.string()).optional().describe(
449
+ "Query parameters to append to the URL (e.g., { timeMin: '2025-01-01T00:00:00Z', maxResults: '10' })"
450
+ ),
451
+ body: z2.record(z2.string(), z2.unknown()).optional().describe("JSON request body for POST/PUT/PATCH")
634
452
  });
635
453
  var outputSchema2 = z2.discriminatedUnion("success", [
636
454
  z2.object({
637
455
  success: z2.literal(true),
638
456
  status: z2.number(),
639
- data: z2.record(z2.string(), z2.unknown())
457
+ data: z2.record(z2.string(), z2.unknown()),
458
+ serviceAccountEmail: z2.string()
640
459
  }),
641
460
  z2.object({
642
461
  success: z2.literal(false),
643
- error: z2.string()
462
+ error: z2.string(),
463
+ serviceAccountEmail: z2.string().optional()
644
464
  })
645
465
  ]);
646
- var requestTool = new ConnectorTool({
647
- name: "request",
648
- description: `Send authenticated requests to the Google Calendar API v3.
649
- Authentication is handled automatically using a service account.
650
- {calendarId} in the path is automatically replaced with the connection's default calendar ID.`,
466
+ var requestWithDelegationTool = new ConnectorTool({
467
+ name: "request_with_delegation",
468
+ description: "Call the Google Calendar 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 a read-only Calendar scope (e.g., ['https://www.googleapis.com/auth/calendar.readonly']). Use this tool when the project knowledge records the calendar with `(delegation, subject: <email>, ...)`. Requires Domain-wide Delegation to be authorized for the service account in the Workspace admin console.",
651
469
  inputSchema: inputSchema2,
652
470
  outputSchema: outputSchema2,
653
- async execute({ connectionId, method, path: path2, queryParams, body, subject }, connections) {
471
+ async execute({ connectionId, method, path: path2, subject, scopes, queryParams, body }, connections) {
654
472
  const connection2 = connections.find((c) => c.id === connectionId);
655
473
  if (!connection2) {
656
474
  return {
@@ -658,41 +476,41 @@ Authentication is handled automatically using a service account.
658
476
  error: `Connection ${connectionId} not found`
659
477
  };
660
478
  }
479
+ const keyJsonBase64 = parameters.serviceAccountKeyJsonBase64.getValue(connection2);
480
+ let serviceAccount;
481
+ try {
482
+ serviceAccount = decodeServiceAccount2(keyJsonBase64);
483
+ } catch (err) {
484
+ const msg = err instanceof Error ? err.message : String(err);
485
+ return {
486
+ success: false,
487
+ error: `Failed to decode service account key: ${msg}`
488
+ };
489
+ }
490
+ const serviceAccountEmail = serviceAccount.client_email;
661
491
  console.log(
662
- `[connector-request] google-calendar/${connection2.name}: ${method} ${path2}`
492
+ `[connector-request] google-calendar/${connection2.name}: ${method} ${path2} subject=${subject}`
663
493
  );
664
494
  try {
665
495
  const { GoogleAuth } = await import("google-auth-library");
666
- const keyJsonBase64 = parameters.serviceAccountKeyJsonBase64.getValue(connection2);
667
- const impersonateEmail = impersonateEmailParameter.tryGetValue(connection2);
668
- const calendarId = calendarIdParameter.tryGetValue(connection2) ?? "primary";
669
- const resolvedSubject = subject ?? impersonateEmail;
670
- if (!resolvedSubject) {
671
- return {
672
- success: false,
673
- error: `Missing required parameter: ${impersonateEmailParameter.slug}. Configure the user email for this connection.`
674
- };
675
- }
676
- const credentials = JSON.parse(
677
- Buffer.from(keyJsonBase64, "base64").toString("utf-8")
678
- );
679
496
  const auth = new GoogleAuth({
680
- credentials,
681
- scopes: [
682
- "https://www.googleapis.com/auth/calendar.readonly",
683
- "https://www.googleapis.com/auth/calendar.events.readonly"
684
- ],
685
- clientOptions: { subject: resolvedSubject }
497
+ credentials: {
498
+ client_email: serviceAccount.client_email,
499
+ private_key: serviceAccount.private_key
500
+ },
501
+ scopes,
502
+ clientOptions: { subject }
686
503
  });
687
504
  const token = await auth.getAccessToken();
688
505
  if (!token) {
689
506
  return {
690
507
  success: false,
691
- error: "Failed to obtain access token"
508
+ error: "Failed to obtain access token",
509
+ serviceAccountEmail
692
510
  };
693
511
  }
694
- const resolvedPath = path2.replace(/\{calendarId\}/g, calendarId);
695
- let url = `${BASE_URL3}${resolvedPath.startsWith("/") ? "" : "/"}${resolvedPath}`;
512
+ const normalizedPath = normalizeRequestPath(path2, BASE_PATH_SEGMENT2);
513
+ let url = `${BASE_URL3}${normalizedPath}`;
696
514
  if (queryParams) {
697
515
  const searchParams = new URLSearchParams(queryParams);
698
516
  url += `?${searchParams.toString()}`;
@@ -700,43 +518,265 @@ Authentication is handled automatically using a service account.
700
518
  const controller = new AbortController();
701
519
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
702
520
  try {
521
+ const hasBody = body != null && ["POST", "PUT", "PATCH"].includes(method);
703
522
  const response = await fetch(url, {
704
523
  method,
705
524
  headers: {
706
525
  Authorization: `Bearer ${token}`,
707
526
  "Content-Type": "application/json"
708
527
  },
709
- body: body && ["POST", "PUT", "PATCH"].includes(method) ? JSON.stringify(body) : void 0,
528
+ body: hasBody ? JSON.stringify(body) : void 0,
710
529
  signal: controller.signal
711
530
  });
712
- if (method === "DELETE" && response.status === 204) {
713
- return {
714
- success: true,
715
- status: 204,
716
- data: { message: "Deleted successfully" }
717
- };
718
- }
719
- const data = await response.json();
531
+ const data = await response.json().catch(() => ({}));
720
532
  if (!response.ok) {
721
533
  const errorObj = data?.error;
534
+ const errorMessage = errorObj?.message ?? (typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`);
722
535
  return {
723
536
  success: false,
724
- error: errorObj?.message ?? `HTTP ${response.status} ${response.statusText}`
537
+ error: errorMessage,
538
+ serviceAccountEmail
725
539
  };
726
540
  }
727
- return { success: true, status: response.status, data };
541
+ return {
542
+ success: true,
543
+ status: response.status,
544
+ data,
545
+ serviceAccountEmail
546
+ };
728
547
  } finally {
729
548
  clearTimeout(timeout);
730
549
  }
731
550
  } catch (err) {
732
551
  const msg = err instanceof Error ? err.message : String(err);
733
- return { success: false, error: msg };
552
+ return {
553
+ success: false,
554
+ error: msg,
555
+ serviceAccountEmail
556
+ };
734
557
  }
735
558
  }
736
559
  });
737
560
 
561
+ // ../connectors/src/connectors/google-calendar/setup.ts
562
+ var requestToolName = `google-calendar-service-account_${requestTool.name}`;
563
+ var requestWithDelegationToolName = `google-calendar-service-account_${requestWithDelegationTool.name}`;
564
+ var READONLY_SCOPES = '["https://www.googleapis.com/auth/calendar.readonly"]';
565
+ var SERVICE_ACCOUNT_KEY_PARAM_SLUG = parameters.serviceAccountKeyJsonBase64.slug;
566
+ var googleCalendarOnboarding = new ConnectorOnboarding({
567
+ connectionSetupInstructions: {
568
+ ja: `Google Calendar \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u3092\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3057\u307E\u3059\u3002\u30A2\u30AF\u30BB\u30B9\u65B9\u6CD5\u3092\u9078\u3093\u3067\u3082\u3089\u3044\u3001\u5BFE\u8C61\u30AB\u30EC\u30F3\u30C0\u30FC\u3092 Project Knowledge \u306B\u8A18\u9332\u3057\u307E\u3059\u3002
569
+
570
+ ## \u30B9\u30C6\u30C3\u30D7 0: \u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u53D6\u5F97
571
+
572
+ \u4EE5\u964D\u306E\u30B9\u30C6\u30C3\u30D7\u3067\u30E6\u30FC\u30B6\u30FC\u306B\u6848\u5185\u3059\u308B\u305F\u3081\u306B\u3001\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u53D6\u5F97\u3057\u307E\u3059\u3002
573
+
574
+ \`${requestToolName}\` \u3092\u4EE5\u4E0B\u306E\u5F15\u6570\u3067 1 \u56DE\u547C\u3073\u3001\u30EC\u30B9\u30DD\u30F3\u30B9\u306E \`serviceAccountEmail\` \u3092\u4FDD\u6301\u3059\u308B:
575
+ - \`method\`: \`"GET"\`
576
+ - \`path\`: \`"/users/me/calendarList"\`
577
+ - \`scopes\`: \`${READONLY_SCOPES}\`
578
+
579
+ \u6210\u529F\u30FB\u5931\u6557\u3069\u3061\u3089\u3067\u3082 \`serviceAccountEmail\` \u306F\u8FD4\u3063\u3066\u304F\u308B\u306E\u3067\u3001\u7D50\u679C\u306E\u30EA\u30B9\u30C8\u306F\u3053\u306E\u30B9\u30C6\u30C3\u30D7\u3067\u306F\u7121\u8996\u3057\u3066\u3088\u3044\u3002\u4EE5\u964D\u3053\u306E\u5024\u3092 \`<serviceAccountEmail>\` \u3068\u3057\u3066\u53C2\u7167\u3059\u308B\u3002
580
+
581
+ ## \u30B9\u30C6\u30C3\u30D7 1: \u30A2\u30AF\u30BB\u30B9\u65B9\u6CD5\u3092\u9078\u629E
582
+
583
+ \`askUserQuestion\` \u3067\u6B21\u306E 3 \u629E\u3092\u63D0\u793A\u3059\u308B:
584
+ - \`type\`: \`"select"\`
585
+ - \`question\`: \u300C\u3069\u306E\u30AB\u30EC\u30F3\u30C0\u30FC\u306B\u30A2\u30AF\u30BB\u30B9\u3057\u307E\u3059\u304B\uFF1F\u300D
586
+ - \`options\`:
587
+ - \`{ label: "\u3053\u306E\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u306B\u5171\u6709\u3055\u308C\u3066\u3044\u308B\u30AB\u30EC\u30F3\u30C0\u30FC\u3092\u4F7F\u3046", value: "service-account" }\`
588
+ - \`{ label: "\u30C9\u30E1\u30A4\u30F3\u5168\u4F53\u306E\u59D4\u4EFB\u3067\u7D44\u7E54\u306E\u30E6\u30FC\u30B6\u30FC\u306E\u30AB\u30EC\u30F3\u30C0\u30FC\u306B\u30A2\u30AF\u30BB\u30B9\u3059\u308B", value: "delegation" }\`
589
+ - \`{ label: "\u4E21\u65B9\u3092\u7D44\u307F\u5408\u308F\u305B\u308B", value: "both" }\`
590
+
591
+ ## \u30B9\u30C6\u30C3\u30D7 2: \u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u7D4C\u8DEF (\`"service-account"\` \u307E\u305F\u306F \`"both"\` \u306E\u5834\u5408)
592
+
593
+ \u30E6\u30FC\u30B6\u30FC\u304C\u3042\u3089\u304B\u3058\u3081\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8 \`<serviceAccountEmail>\` \u306B\u5171\u6709\u8A2D\u5B9A\u3057\u305F\u30AB\u30EC\u30F3\u30C0\u30FC\u306E ID \u3092\u76F4\u63A5\u53D7\u3051\u53D6\u308A\u3001\u758E\u901A\u78BA\u8A8D\u3057\u307E\u3059\u3002multiSelect \u306F\u51FA\u3055\u306A\u3044 (\u30E6\u30FC\u30B6\u30FC\u304C\u5165\u529B\u3057\u305F\u6642\u70B9\u3067\u5BFE\u8C61\u304C\u78BA\u5B9A\u3057\u3066\u3044\u308B\u305F\u3081)\u3002
594
+
595
+ 1. \`askUserQuestion\` \u3067\u30AB\u30EC\u30F3\u30C0\u30FC ID \u3092\u805E\u304F:
596
+ - \`type\`: \`"freeText"\`
597
+ - \`question\`: \u300C\u30A2\u30AF\u30BB\u30B9\u3057\u305F\u3044\u30AB\u30EC\u30F3\u30C0\u30FC\u306E ID \u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u53EF\u3001\u30AB\u30F3\u30DE\u533A\u5207\u308A\uFF09\u3002\u3042\u3089\u304B\u3058\u3081 \`<serviceAccountEmail>\` \u306B\u5171\u6709\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002\u30E1\u30A4\u30F3\u30AB\u30EC\u30F3\u30C0\u30FC\u306F\u6240\u6709\u8005\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u305D\u306E\u307E\u307E\u5165\u529B\u3067\u304D\u307E\u3059\u3002\u305D\u308C\u4EE5\u5916\u306E\u30AB\u30EC\u30F3\u30C0\u30FC\uFF08\u30C1\u30FC\u30E0\u30AB\u30EC\u30F3\u30C0\u30FC\u7B49\uFF09\u306F Google Calendar \u306E\u8A2D\u5B9A\u304B\u3089\u300C\u30AB\u30EC\u30F3\u30C0\u30FC ID\u300D\u3092\u30B3\u30D4\u30FC\u3057\u3066\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u300D
598
+ - \`placeholder\`: \`"alice@example.com, c_xxx@group.calendar.google.com"\`
599
+
600
+ 2. \u5165\u529B\u3055\u308C\u305F\u5404\u30AB\u30EC\u30F3\u30C0\u30FC ID \`<id>\` \u306B\u3064\u3044\u3066 \`${requestToolName}\` \u3067\u758E\u901A\u78BA\u8A8D:
601
+ - \`method\`: \`"GET"\`
602
+ - \`path\`: \`"/calendars/<id>"\`
603
+ - \`scopes\`: \`${READONLY_SCOPES}\`
604
+ - \u6210\u529F\u3059\u308C\u3070\u3001\u30EC\u30B9\u30DD\u30F3\u30B9 \`data.summary\` \u3092\u30AB\u30EC\u30F3\u30C0\u30FC\u540D\u3068\u3057\u3066\u4FDD\u6301
605
+ - \u5931\u6557 (404 / 403 \u7B49) \u306F\u30A2\u30AF\u30BB\u30B9\u4E0D\u53EF\u3068\u3057\u3066\u8A18\u9332
606
+
607
+ 3. \u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
608
+ - **\u5168 ID \u5931\u6557**: \u30A2\u30AF\u30BB\u30B9\u65B9\u6CD5\u304C \`"service-account"\` \u3060\u3051\u306A\u3089\u30B9\u30C6\u30C3\u30D7 5 \u3078\u3002\`"both"\` \u306E\u5834\u5408\u306F\u305D\u306E\u307E\u307E\u30B9\u30C6\u30C3\u30D7 3 \u3078\u9032\u3080
609
+ - **\u4E00\u90E8\u307E\u305F\u306F\u5168\u4EF6\u6210\u529F**: \u6210\u529F\u3057\u305F\u30AB\u30EC\u30F3\u30C0\u30FC\u3092\u305D\u306E\u307E\u307E\u30B9\u30C6\u30C3\u30D7 4 \u7528\u306E\u9078\u629E\u7D50\u679C\u3068\u3057\u3066\u78BA\u5B9A\u3059\u308B\u3002\u5931\u6557\u304C\u3042\u308C\u3070\u3001\u305D\u306E\u65E8\u3092 1 \u6587\u3067\u77ED\u304F\u4F1D\u3048\u308B
610
+
611
+ ## \u30B9\u30C6\u30C3\u30D7 3: \u30C9\u30E1\u30A4\u30F3\u5168\u4F53\u306E\u59D4\u4EFB\u7D4C\u8DEF (\`"delegation"\` \u307E\u305F\u306F \`"both"\` \u306E\u5834\u5408)
612
+
613
+ \u5404 Workspace \u30E6\u30FC\u30B6\u30FC\u304C\u6301\u3063\u3066\u3044\u308B\u591A\u6570\u306E\u30AB\u30EC\u30F3\u30C0\u30FC\u304B\u3089\u3001\u5BFE\u8C61\u3092\u7D5E\u308A\u8FBC\u307F\u307E\u3059\u3002
614
+
615
+ 1. \`askUserQuestion\` \u3067\u5BFE\u8C61\u30E6\u30FC\u30B6\u30FC\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u805E\u304F:
616
+ - \`type\`: \`"freeText"\`
617
+ - \`question\`: \u300C\u30A2\u30AF\u30BB\u30B9\u3057\u305F\u3044\u30AB\u30EC\u30F3\u30C0\u30FC\u3092\u6240\u6709\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\u3002Workspace \u7BA1\u7406\u8005\u304C\u3042\u3089\u304B\u3058\u3081\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8 \`<serviceAccountEmail>\` \u306B\u5BFE\u3057\u3066\u30C9\u30E1\u30A4\u30F3\u5168\u4F53\u306E\u59D4\u4EFB\u3092\u627F\u8A8D\u3057\u3066\u3044\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08[\u7BA1\u7406\u8005\u5411\u3051\u8A2D\u5B9A\u30AC\u30A4\u30C9](https://support.google.com/a/answer/162106)\uFF09\u3002\u300D
618
+ - \`placeholder\`: \`"alice@example.com, bob@example.com"\`
619
+
620
+ 2. \u5165\u529B\u3055\u308C\u305F\u5404\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9 \`<email>\` \u306B\u3064\u3044\u3066 \`${requestWithDelegationToolName}\` \u3092\u547C\u3073\u3001\u305D\u306E\u30E6\u30FC\u30B6\u30FC\u304C\u30A2\u30AF\u30BB\u30B9\u3067\u304D\u308B\u30AB\u30EC\u30F3\u30C0\u30FC\u4E00\u89A7\u3092\u53D6\u5F97:
621
+ - \`method\`: \`"GET"\`
622
+ - \`path\`: \`"/users/me/calendarList"\`
623
+ - \`subject\`: \`<email>\`
624
+ - \`scopes\`: \`${READONLY_SCOPES}\`
625
+
626
+ 3. \u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
627
+ - **\u5168 email \u3067\u5931\u6557**: \u30A2\u30AF\u30BB\u30B9\u65B9\u6CD5\u304C \`"delegation"\` \u3060\u3051\u3001\u307E\u305F\u306F \`"both"\` \u3067\u30B9\u30C6\u30C3\u30D7 2 \u3067\u3082 0 \u4EF6\u306E\u3068\u304D\u306F\u30B9\u30C6\u30C3\u30D7 5 \u3078\u3002\`"both"\` \u3067\u30B9\u30C6\u30C3\u30D7 2 \u306B\u30AB\u30EC\u30F3\u30C0\u30FC\u304C\u3042\u308C\u3070\u3001\u305D\u306E\u307E\u307E\u30B9\u30C6\u30C3\u30D7 4 \u3078\u9032\u3080
628
+ - **\u4E00\u90E8\u307E\u305F\u306F\u5168\u4EF6\u6210\u529F**: \`askUserQuestion\` \u3067\u30AB\u30EC\u30F3\u30C0\u30FC\u3092\u7D5E\u308A\u8FBC\u307E\u305B\u308B
629
+ - \`type\`: \`"multiSelect"\`
630
+ - \`question\`: \u300C\u4F7F\u7528\u3059\u308B\u30AB\u30EC\u30F3\u30C0\u30FC\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u8907\u6570\u53EF\uFF09\u300D
631
+ - \`options\`: \u767A\u898B\u3057\u305F\u5404\u30AB\u30EC\u30F3\u30C0\u30FC\u306B\u3064\u3044\u3066 \`label\`: \`"<\u30AB\u30EC\u30F3\u30C0\u30FC\u540D> (\u6240\u6709\u8005: <\u305D\u306E\u3068\u304D\u306E subject>)"\`\u3001\`value\`: \`"<calendarId>"\`
632
+ - \u4E00\u90E8\u5931\u6557\u304C\u3042\u308C\u3070\u3001\u305D\u306E\u65E8\u3092 1 \u6587\u3067\u77ED\u304F\u4F1D\u3048\u308B
633
+
634
+ ## \u30B9\u30C6\u30C3\u30D7 4: Project Knowledge \u306B\u8A18\u9332
635
+
636
+ \u30B9\u30C6\u30C3\u30D7 2 \u3068 3 \u3067\u78BA\u5B9A\u3057\u305F calendarId \u96C6\u5408\u3092\u7D71\u5408\u3059\u308B\u3002\u5404 calendarId \u306B\u3064\u3044\u3066\u3001\u30B9\u30C6\u30C3\u30D7 2 \u3067\u758E\u901A\u78BA\u8A8D\u3057\u305F\u7D50\u679C\u304B\u3001\u30B9\u30C6\u30C3\u30D7 3 \u306E\u30C7\u30A3\u30B9\u30AB\u30D0\u30EA\u7D50\u679C\u304B\u3089\u7D4C\u8DEF\u3068 (delegation \u306E\u5834\u5408\u306F) subject \u3092\u7279\u5B9A\u3059\u308B\u3002\`finalizeSetup\` \u3092\u547C\u3073\u3001\u5404\u30AB\u30EC\u30F3\u30C0\u30FC\u3092\u300C\u30AB\u30EC\u30F3\u30C0\u30FC\u540D\u30FB\u30AB\u30EC\u30F3\u30C0\u30FC ID\u30FB\u30A2\u30AF\u30BB\u30B9\u7D4C\u8DEF\uFF08delegation \u304B service-account \u304B\uFF09\u30FBdelegation \u306E\u5834\u5408\u306F subject\u300D\u3068\u3068\u3082\u306B\u3053\u306E\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30B9\u30B3\u30FC\u30D7\u60C5\u5831\u3068\u3057\u3066\u8A18\u9332\u3059\u308B\u3002
637
+
638
+ ## \u30B9\u30C6\u30C3\u30D7 5: \u6700\u7D42\u7684\u306B\u30AB\u30EC\u30F3\u30C0\u30FC\u304C 0 \u4EF6\u306E\u3068\u304D\u306E\u30A8\u30B9\u30AB\u30EC\u30FC\u30B7\u30E7\u30F3
639
+
640
+ \u6700\u7D42\u7684\u306B\u5BFE\u8C61\u30AB\u30EC\u30F3\u30C0\u30FC\u304C 1 \u4EF6\u3082\u306A\u3044\u5834\u5408\u3001\`askUserQuestion\` \u3067\u6B21\u306E\u3088\u3046\u306B\u6848\u5185\u3059\u308B:
641
+
642
+ - \`question\`: \u300C\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u30AB\u30EC\u30F3\u30C0\u30FC\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8 \`<serviceAccountEmail>\` \u3067\u6B21\u306E\u3044\u305A\u308C\u304B\u3092\u884C\u3063\u3066\u304B\u3089\u7D9A\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u300D
643
+ - \`options\`: \u3053\u3053\u307E\u3067\u306B\u8A66\u3057\u305F\u7D4C\u8DEF\u306B\u5FDC\u3058\u3066\u4EE5\u4E0B\u3092\u7D44\u307F\u5408\u308F\u305B\u308B:
644
+ - \u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u7D4C\u8DEF\u3092\u8A66\u3057\u305F\u5834\u5408: \`{ label: "\u5BFE\u8C61\u30AB\u30EC\u30F3\u30C0\u30FC\u3092\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u306B\u5171\u6709\u3057\u305F\u306E\u3067\u30EA\u30C8\u30E9\u30A4", value: "retry" }\`
645
+ - \u30C9\u30E1\u30A4\u30F3\u5168\u4F53\u306E\u59D4\u4EFB\u7D4C\u8DEF\u3092\u8A66\u3057\u305F\u5834\u5408: \`{ label: "\u30C9\u30E1\u30A4\u30F3\u5168\u4F53\u306E\u59D4\u4EFB\u3092\u627F\u8A8D\u3057\u305F\u306E\u3067\u30EA\u30C8\u30E9\u30A4", value: "retry" }\`
646
+ - \u5E38\u306B: \`{ label: "\u30A2\u30AF\u30BB\u30B9\u65B9\u6CD5\u3092\u5909\u66F4\u3059\u308B", value: "restart" }\`
647
+ - \u5E38\u306B: \`{ label: "\u5225\u306E\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u3067\u8A2D\u5B9A\u3057\u76F4\u3059", value: "change-service-account" }\`
648
+
649
+ \u9078\u629E\u7D50\u679C\u306B\u5FDC\u3058\u305F\u6319\u52D5:
650
+ - "retry": \u76F4\u524D\u306E\u7D4C\u8DEF\u3092\u518D probe (\u30EA\u30C8\u30E9\u30A4\u306E\u4ED5\u65B9\u306F\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u304C\u6587\u8108\u306B\u5FDC\u3058\u3066\u5224\u65AD)
651
+ - "restart": \u30B9\u30C6\u30C3\u30D7 1 \u304B\u3089\u518D\u5B9F\u884C
652
+ - "change-service-account": \`updateConnectionParameters\` \u3092\u547C\u3093\u3067\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8 JSON \u3092\u518D\u5165\u529B\u3055\u305B\u308B:
653
+ - \`parameterSlug\`: \`"${SERVICE_ACCOUNT_KEY_PARAM_SLUG}"\`
654
+ - \u5B8C\u4E86\u5F8C\u3001\u30B9\u30C6\u30C3\u30D7 0 \u304B\u3089\u518D\u5B9F\u884C
655
+
656
+ ## \u5236\u7D04
657
+
658
+ - \u4E0A\u8A18\u4EE5\u5916\u306E API \u547C\u3073\u51FA\u3057\u3092 setup \u4E2D\u306B\u884C\u308F\u306A\u3044
659
+ - \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`,
660
+ en: `Set up the Google Calendar connection. Ask the user how they want to access calendars and record the target calendars in Project Knowledge.
661
+
662
+ ## Step 0: Get the service account email
663
+
664
+ We need the service account email so we can show it to the user in the next steps.
665
+
666
+ Call \`${requestToolName}\` once and remember the \`serviceAccountEmail\` from the response:
667
+ - \`method\`: \`"GET"\`
668
+ - \`path\`: \`"/users/me/calendarList"\`
669
+ - \`scopes\`: \`${READONLY_SCOPES}\`
670
+
671
+ \`serviceAccountEmail\` is returned regardless of success or failure, so the list payload itself can be ignored at this stage. Refer to the captured value as \`<serviceAccountEmail>\` in the steps below.
672
+
673
+ ## Step 1: Choose the access method
674
+
675
+ Call \`askUserQuestion\`:
676
+ - \`type\`: \`"select"\`
677
+ - \`question\`: "Which calendars do you want to access?"
678
+ - \`options\`:
679
+ - \`{ label: "Calendars shared with this service account", value: "service-account" }\`
680
+ - \`{ label: "Calendars of users in your organization (via Domain-wide Delegation)", value: "delegation" }\`
681
+ - \`{ label: "Both", value: "both" }\`
682
+
683
+ ## Step 2: Service-account path (when \`"service-account"\` or \`"both"\`)
684
+
685
+ Take calendar IDs that the user has already shared with \`<serviceAccountEmail>\`, and verify each one. No multiSelect step (the user has already specified which calendars to use by entering their IDs).
686
+
687
+ 1. Ask the user for calendar IDs:
688
+ - \`type\`: \`"freeText"\`
689
+ - \`question\`: "Enter the IDs of the calendars you want to access (comma-separated for multiple). Each calendar must already be shared with \`<serviceAccountEmail>\`. For someone's primary calendar you can enter their email address directly. For other calendars (such as team calendars), copy the 'Calendar ID' from the calendar settings in Google Calendar."
690
+ - \`placeholder\`: \`"alice@example.com, c_xxx@group.calendar.google.com"\`
691
+
692
+ 2. For each calendar ID \`<id>\`, verify access by calling \`${requestToolName}\`:
693
+ - \`method\`: \`"GET"\`
694
+ - \`path\`: \`"/calendars/<id>"\`
695
+ - \`scopes\`: \`${READONLY_SCOPES}\`
696
+ - On success, capture \`data.summary\` as the calendar name
697
+ - On failure (404 / 403 / etc.), record the ID as inaccessible
698
+
699
+ 3. Branch on the results:
700
+ - **All IDs failed**: if the access method is \`"service-account"\` only, jump to Step 5. If \`"both"\`, continue to Step 3.
701
+ - **Any success**: take the verified calendars as the selection for Step 4. If some failed, mention them briefly in one sentence.
702
+
703
+ ## Step 3: Domain-wide Delegation path (when \`"delegation"\` or \`"both"\`)
704
+
705
+ Discover calendars from each Workspace user and let the user narrow them down.
706
+
707
+ 1. Ask the user for the target Workspace user emails:
708
+ - \`type\`: \`"freeText"\`
709
+ - \`question\`: "Enter the email addresses of the Google Workspace users whose calendars you want to access (comma-separated for multiple). Your Workspace admin must have authorized Domain-wide Delegation for service account \`<serviceAccountEmail>\` in advance ([admin setup guide](https://support.google.com/a/answer/162106))."
710
+ - \`placeholder\`: \`"alice@example.com, bob@example.com"\`
711
+
712
+ 2. For each email \`<email>\`, list calendars accessible to that user via \`${requestWithDelegationToolName}\`:
713
+ - \`method\`: \`"GET"\`
714
+ - \`path\`: \`"/users/me/calendarList"\`
715
+ - \`subject\`: \`<email>\`
716
+ - \`scopes\`: \`${READONLY_SCOPES}\`
717
+
718
+ 3. Branch on the results:
719
+ - **All emails failed**: if the access method is \`"delegation"\` only, or \`"both"\` with no calendars from Step 2, jump to Step 5. If \`"both"\` and Step 2 had calendars, continue to Step 4.
720
+ - **Any success**: ask the user to narrow down via \`askUserQuestion\`:
721
+ - \`type\`: \`"multiSelect"\`
722
+ - \`question\`: "Select the calendars to use (multiple allowed)"
723
+ - \`options\`: For each discovered calendar, \`label\`: \`"<calendar name> (owner: <the subject used>)"\`, \`value\`: \`"<calendarId>"\`
724
+ - If some emails failed, mention them briefly in one sentence.
725
+
726
+ ## Step 4: Record in Project Knowledge
727
+
728
+ Aggregate the calendarIds from Steps 2 and 3. Cross-reference each calendarId against the verification result (Step 2) or the discovery result (Step 3) to recover its access path and, for delegation, the subject. Call \`finalizeSetup\` and record each calendar with its name, calendar ID, access path (delegation or service-account), and (for delegation) the subject as this connection's scope info.
729
+
730
+ ## Step 5: Escalation when zero calendars are selected
731
+
732
+ When no calendars end up selected, call \`askUserQuestion\`:
733
+
734
+ - \`question\`: "No accessible calendars found. Please do one of the following before continuing. Service account: \`<serviceAccountEmail>\`."
735
+ - \`options\`: Combine these based on which paths were attempted:
736
+ - Service-account path attempted: \`{ label: "Shared the calendar with the service account \u2014 retry", value: "retry" }\`
737
+ - Domain-wide Delegation path attempted: \`{ label: "Authorized Domain-wide Delegation \u2014 retry", value: "retry" }\`
738
+ - Always: \`{ label: "Change the access method", value: "restart" }\`
739
+ - Always: \`{ label: "Use a different service account", value: "change-service-account" }\`
740
+
741
+ Behavior per selection:
742
+ - "retry": re-probe the path that was just attempted (the agent decides the exact retry plan)
743
+ - "restart": re-run from Step 1
744
+ - "change-service-account": call \`updateConnectionParameters\` to have the user re-upload the service account JSON:
745
+ - \`parameterSlug\`: \`"${SERVICE_ACCOUNT_KEY_PARAM_SLUG}"\`
746
+ - After it completes, re-run from Step 0
747
+
748
+ ## Constraints
749
+
750
+ - Do not call any other API endpoints during setup
751
+ - Write at most 1 sentence between tool calls`
752
+ },
753
+ dataOverviewInstructions: {
754
+ en: `For each calendar configured for this connection, fetch metadata and a small sample of upcoming events. Pick the tool that matches the calendar's access path:
755
+ - Calendars accessed via delegation \u2192 \`${requestWithDelegationToolName}\` with the user's email as \`subject\`
756
+ - Calendars shared directly with the service account \u2192 \`${requestToolName}\`
757
+
758
+ Pass \`scopes: ${READONLY_SCOPES}\` for every call.
759
+
760
+ For each calendar:
761
+ 1. \`method=GET\`, \`path=/calendars/<id>\` to fetch metadata.
762
+ 2. \`method=GET\`, \`path=/calendars/<id>/events\`, \`queryParams={ timeMin: <RFC3339 now>, maxResults: "10", singleEvents: "true", orderBy: "startTime" }\`.`,
763
+ ja: `\u3053\u306E\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u3067\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u308B\u5404\u30AB\u30EC\u30F3\u30C0\u30FC\u306B\u3064\u3044\u3066\u3001\u30E1\u30BF\u30C7\u30FC\u30BF\u3068\u76F4\u8FD1\u306E\u30A4\u30D9\u30F3\u30C8\u3092\u5C11\u91CF\u53D6\u5F97\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30A2\u30AF\u30BB\u30B9\u7D4C\u8DEF\u306B\u5FDC\u3058\u3066\u30C4\u30FC\u30EB\u3092\u9078\u3073\u307E\u3059:
764
+ - delegation \u7D4C\u7531\u306E\u30AB\u30EC\u30F3\u30C0\u30FC \u2192 \`${requestWithDelegationToolName}\` \u3092\u3001\u5BFE\u8C61\u30E6\u30FC\u30B6\u30FC\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092 \`subject\` \u3068\u3057\u3066\u547C\u3076
765
+ - service account \u306B\u76F4\u63A5\u5171\u6709\u3055\u308C\u305F\u30AB\u30EC\u30F3\u30C0\u30FC \u2192 \`${requestToolName}\` \u3092\u547C\u3076
766
+
767
+ \`scopes\` \u306F\u6BCE\u56DE \`${READONLY_SCOPES}\` \u3092\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\u3002
768
+
769
+ \u5404\u30AB\u30EC\u30F3\u30C0\u30FC\u306B\u3064\u3044\u3066:
770
+ 1. \`method=GET\`\u3001\`path=/calendars/<id>\` \u3067\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u53D6\u5F97
771
+ 2. \`method=GET\`\u3001\`path=/calendars/<id>/events\`\u3001\`queryParams={ timeMin: <RFC3339 \u306E\u73FE\u5728\u6642\u523B>, maxResults: "10", singleEvents: "true", orderBy: "startTime" }\``
772
+ }
773
+ });
774
+
738
775
  // ../connectors/src/connectors/google-calendar/index.ts
739
- var tools = { request: requestTool, listCalendars: listCalendarsTool };
776
+ var tools = {
777
+ request: requestTool,
778
+ request_with_delegation: requestWithDelegationTool
779
+ };
740
780
  var googleCalendarConnector = new ConnectorPlugin({
741
781
  slug: "google-calendar",
742
782
  authType: AUTH_TYPES.SERVICE_ACCOUNT,
@@ -745,62 +785,50 @@ var googleCalendarConnector = new ConnectorPlugin({
745
785
  iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/2YsqoBEpdELmfDeFcyGHyE/4494c633b5ae15e562cb739cd85442c1/google-calendar.png",
746
786
  parameters,
747
787
  releaseFlag: { dev1: true, dev2: true, prod: true },
788
+ categories: ["productivity"],
748
789
  onboarding: googleCalendarOnboarding,
749
790
  systemPrompt: {
750
791
  en: `### Tools
751
792
 
752
- - \`google-calendar-service-account_request\`: The only way to call the Google Calendar API. Use it to list calendars, get events, and manage calendar data. Authentication is handled automatically using a service account with Domain-wide Delegation \u2014 the service account impersonates the user configured on the connection (\`impersonate-email\` parameter). The {calendarId} placeholder in paths is automatically replaced with the configured default calendar ID. Pass an optional \`subject\` only if you need to override the configured user for a specific request.
793
+ This connector exposes two request tools that correspond to the two ways a Service Account can authenticate against the Google Calendar API:
753
794
 
754
- ### Business Logic
795
+ - \`google-calendar-service-account_request_with_delegation\`: Call the Calendar API on behalf of the specified Workspace user via Domain-wide Delegation. Pass \`subject\` as the target user email. Requires Domain-wide Delegation to be authorized for the service account in the Workspace admin console.
796
+ - \`google-calendar-service-account_request\`: Call the Calendar API as the service account itself (no delegation). Only calendars explicitly shared with the service account email are accessible.
755
797
 
756
- The business logic type for this connector is "typescript". Use the connector SDK in your handler. Do NOT read credentials from environment variables.
798
+ Both tools require a \`scopes\` argument.
757
799
 
758
- SDK methods (client created via \`connection(connectionId)\` \u2014 the connection's \`impersonate-email\` parameter is used automatically for Domain-wide Delegation):
759
- - \`client.listCalendars()\` \u2014 list all accessible calendars
760
- - \`client.listEvents(options?, calendarId?)\` \u2014 list events with optional filters
761
- - \`client.getEvent(eventId, calendarId?)\` \u2014 get a single event by ID
762
- - \`client.request(path, init?)\` \u2014 low-level authenticated fetch
800
+ ### OAuth Scopes (pass as \`scopes\` argument)
763
801
 
764
- #### Domain-wide Delegation
802
+ This connector is currently read-only. Pass one of:
765
803
 
766
- The target user email is configured on the connection (\`impersonate-email\` parameter), so \`connection()\` automatically uses it. Pass \`subject\` only to override it:
804
+ - \`https://www.googleapis.com/auth/calendar.readonly\` \u2014 read-only on calendars and events
805
+ - \`https://www.googleapis.com/auth/calendar.events.readonly\` \u2014 read-only on events (no calendar metadata)
806
+ - \`https://www.googleapis.com/auth/calendar.freebusy\` \u2014 busy/free time queries only
767
807
 
768
- \`\`\`ts
769
- const calendar = connection("<connectionId>", { subject: "other-user@example.com" });
770
- \`\`\`
808
+ For \`request_with_delegation\`, the Workspace admin must have authorized the requested scope for the service account in the Domain-wide Delegation settings, otherwise token issuance will fail with \`unauthorized_client\`.
771
809
 
772
- \`\`\`ts
773
- import type { Context } from "hono";
774
- import { connection } from "@squadbase/vite-server/connectors/google-calendar";
810
+ Per-endpoint scope reference: https://developers.google.com/calendar/api/auth
775
811
 
776
- const calendar = connection("<connectionId>");
812
+ ### Choosing the right tool
777
813
 
778
- export default async function handler(c: Context) {
779
- const now = new Date().toISOString();
780
- const { items } = await calendar.listEvents({
781
- timeMin: now,
782
- maxResults: 10,
783
- singleEvents: true,
784
- orderBy: "startTime",
785
- });
814
+ Each calendar configured for this connection has an access path. Match the path to the tool:
786
815
 
787
- return c.json(
788
- items.map((event) => ({
789
- id: event.id,
790
- summary: event.summary,
791
- start: event.start.dateTime ?? event.start.date,
792
- end: event.end.dateTime ?? event.end.date,
793
- location: event.location,
794
- })),
795
- );
796
- }
797
- \`\`\`
816
+ - A Workspace user's calendar accessed via Domain-wide Delegation \u2192 use \`request_with_delegation\` and pass that user's email as \`subject\`
817
+ - A calendar shared directly with the service account \u2192 use \`request\` (no \`subject\`)
818
+
819
+ ### Path conventions
820
+
821
+ Write the calendar ID directly into the path \u2014 there is no placeholder substitution. Examples:
822
+
823
+ - \`/users/me/calendarList\` \u2014 list calendars accessible to the authenticated identity
824
+ - \`/calendars/alice@example.com/events\` \u2014 events on alice's primary calendar
825
+ - \`/calendars/c_abc123@group.calendar.google.com/events\` \u2014 events on a secondary calendar
798
826
 
799
827
  ### Google Calendar API v3 Reference
800
828
 
801
829
  #### Available Endpoints
802
830
  - GET \`/calendars/{calendarId}\` \u2014 Get calendar metadata
803
- - GET \`/users/me/calendarList\` \u2014 List all calendars accessible by the authenticated user
831
+ - GET \`/users/me/calendarList\` \u2014 List all calendars accessible by the authenticated identity
804
832
  - GET \`/calendars/{calendarId}/events\` \u2014 List events on a calendar
805
833
  - GET \`/calendars/{calendarId}/events/{eventId}\` \u2014 Get a single event
806
834
 
@@ -812,66 +840,93 @@ export default async function handler(c: Context) {
812
840
  - \`orderBy=startTime\` \u2014 Order by start time (requires singleEvents=true)
813
841
  - \`q\` \u2014 Free text search terms
814
842
 
815
- #### Tips
816
- - Use \`{calendarId}\` placeholder in paths \u2014 it is automatically replaced with the configured default calendar ID
817
- - Set \`singleEvents=true\` to expand recurring events into individual instances
818
- - When using \`orderBy=startTime\`, you must also set \`singleEvents=true\`
819
- - Use RFC3339 format for time parameters (e.g., "2024-01-15T09:00:00Z" or "2024-01-15T09:00:00+09:00")
820
- - The default calendar ID is "primary" if not configured`,
821
- ja: `### \u30C4\u30FC\u30EB
822
-
823
- - \`google-calendar-service-account_request\`: Google Calendar API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u4E00\u89A7\u53D6\u5F97\u3001\u30A4\u30D9\u30F3\u30C8\u306E\u53D6\u5F97\u3001\u30AB\u30EC\u30F3\u30C0\u30FC\u30C7\u30FC\u30BF\u306E\u7BA1\u7406\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\uFF0BDomain-wide Delegation\u3067\u8A8D\u8A3C\u304C\u81EA\u52D5\u7684\u306B\u51E6\u7406\u3055\u308C\u3001\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306B\u8A2D\u5B9A\u3055\u308C\u305F\u30E6\u30FC\u30B6\u30FC\uFF08\`impersonate-email\`\u30D1\u30E9\u30E1\u30FC\u30BF\uFF09\u3068\u3057\u3066\u52D5\u4F5C\u3057\u307E\u3059\u3002\u30D1\u30B9\u5185\u306E{calendarId}\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u306F\u8A2D\u5B9A\u6E08\u307F\u306E\u30C7\u30D5\u30A9\u30EB\u30C8\u30AB\u30EC\u30F3\u30C0\u30FCID\u3067\u81EA\u52D5\u7684\u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002\u7279\u5B9A\u30EA\u30AF\u30A8\u30B9\u30C8\u3067\u8A2D\u5B9A\u30E6\u30FC\u30B6\u30FC\u3092\u4E0A\u66F8\u304D\u3057\u305F\u3044\u5834\u5408\u306E\u307F\u3001\u30AA\u30D7\u30B7\u30E7\u30F3\u306E\`subject\`\u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\u3002
824
-
825
843
  ### Business Logic
826
844
 
827
- \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u30CF\u30F3\u30C9\u30E9\u5185\u3067\u306F\u30B3\u30CD\u30AF\u30BFSDK\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u8A8D\u8A3C\u60C5\u5831\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
845
+ The business logic type for this connector is "typescript". Use the connector SDK in your handler. Do NOT read credentials from environment variables.
828
846
 
829
- SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8 \u2014 \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\`impersonate-email\`\u30D1\u30E9\u30E1\u30FC\u30BF\u304C\u81EA\u52D5\u7684\u306BDomain-wide Delegation\u306Esubject\u3068\u3057\u3066\u4F7F\u308F\u308C\u307E\u3059):
830
- - \`client.listCalendars()\` \u2014 \u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u5168\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u4E00\u89A7\u53D6\u5F97
831
- - \`client.listEvents(options?, calendarId?)\` \u2014 \u30D5\u30A3\u30EB\u30BF\u30FC\u4ED8\u304D\u30A4\u30D9\u30F3\u30C8\u4E00\u89A7\u53D6\u5F97
832
- - \`client.getEvent(eventId, calendarId?)\` \u2014 ID\u306B\u3088\u308B\u5358\u4E00\u30A4\u30D9\u30F3\u30C8\u53D6\u5F97
833
- - \`client.request(path, init?)\` \u2014 \u4F4E\u30EC\u30D9\u30EB\u306E\u8A8D\u8A3C\u4ED8\u304Dfetch
847
+ SDK methods (client created via \`connection(connectionId)\`):
834
848
 
835
- #### Domain-wide Delegation
849
+ - \`client.requestWithDelegation(path, { subject, scopes, init? })\` \u2014 call the API as the impersonated Workspace user via Domain-wide Delegation
850
+ - \`client.request(path, { scopes, init? })\` \u2014 call the API as the service account itself (only calendars shared with the SA email are accessible)
836
851
 
837
- \u5BFE\u8C61\u30E6\u30FC\u30B6\u30FC\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306F\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\uFF08\`impersonate-email\`\u30D1\u30E9\u30E1\u30FC\u30BF\uFF09\u306B\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u308B\u305F\u3081\u3001\`connection()\`\u306F\u81EA\u52D5\u7684\u306B\u305D\u308C\u3092\u4F7F\u3044\u307E\u3059\u3002\u4E0A\u66F8\u304D\u3057\u305F\u3044\u5834\u5408\u306E\u307F\`subject\`\u3092\u6E21\u3057\u307E\u3059\uFF1A
852
+ Both methods take \`scopes\` \u2014 pass the minimum scope(s) for the endpoint. Both return a standard \`Response\`. Read the body with \`.json()\`. Same path conventions as the tools.
838
853
 
839
- \`\`\`ts
840
- const calendar = connection("<connectionId>", { subject: "other-user@example.com" });
841
- \`\`\`
854
+ #### Example
842
855
 
843
856
  \`\`\`ts
844
857
  import type { Context } from "hono";
845
858
  import { connection } from "@squadbase/vite-server/connectors/google-calendar";
846
859
 
847
860
  const calendar = connection("<connectionId>");
861
+ const READ = ["https://www.googleapis.com/auth/calendar.readonly"];
848
862
 
849
863
  export default async function handler(c: Context) {
850
864
  const now = new Date().toISOString();
851
- const { items } = await calendar.listEvents({
865
+ const qs = new URLSearchParams({
852
866
  timeMin: now,
853
- maxResults: 10,
854
- singleEvents: true,
867
+ maxResults: "10",
868
+ singleEvents: "true",
855
869
  orderBy: "startTime",
856
870
  });
857
871
 
858
- return c.json(
859
- items.map((event) => ({
860
- id: event.id,
861
- summary: event.summary,
862
- start: event.start.dateTime ?? event.start.date,
863
- end: event.end.dateTime ?? event.end.date,
864
- location: event.location,
865
- })),
872
+ // alice@example.com is accessed via delegation
873
+ const aliceRes = await calendar.requestWithDelegation(
874
+ \`/calendars/alice@example.com/events?\${qs}\`,
875
+ { subject: "alice@example.com", scopes: READ },
866
876
  );
877
+ const alice = await aliceRes.json();
878
+
879
+ // team@example.com is shared directly with the service account
880
+ const teamRes = await calendar.request(
881
+ \`/calendars/team@example.com/events?\${qs}\`,
882
+ { scopes: READ },
883
+ );
884
+ const team = await teamRes.json();
885
+
886
+ return c.json({ alice: alice.items, team: team.items });
867
887
  }
868
- \`\`\`
888
+ \`\`\``,
889
+ ja: `### \u30C4\u30FC\u30EB
890
+
891
+ \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u30FC\u306F\u3001Service Account \u304C Google Calendar API \u306B\u8A8D\u8A3C\u3059\u308B 2 \u3064\u306E\u65B9\u6CD5\u306B\u5BFE\u5FDC\u3059\u308B 2 \u3064\u306E request \u30C4\u30FC\u30EB\u3092\u516C\u958B\u3057\u307E\u3059:
892
+
893
+ - \`google-calendar-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 Calendar 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\u3002Workspace \u7BA1\u7406\u30B3\u30F3\u30BD\u30FC\u30EB\u3067 Service Account \u306E Domain-wide Delegation \u304C\u627F\u8A8D\u3055\u308C\u3066\u3044\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002
894
+ - \`google-calendar-service-account_request\`: Service Account \u81EA\u8EAB\u3068\u3057\u3066 Calendar API \u3092\u547C\u3073\u51FA\u3057\u307E\u3059 (Domain-wide Delegation \u3092\u4F7F\u308F\u306A\u3044\u7D4C\u8DEF)\u3002Service Account \u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306B\u660E\u793A\u7684\u306B\u5171\u6709\u3055\u308C\u3066\u3044\u308B\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u307F\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u3067\u3059\u3002
895
+
896
+ \u4E21\u30C4\u30FC\u30EB\u3068\u3082 \`scopes\` \u5F15\u6570\u304C\u5FC5\u9808\u3067\u3059\u3002
897
+
898
+ ### OAuth \u30B9\u30B3\u30FC\u30D7 (\`scopes\` \u5F15\u6570\u3067\u6E21\u3059)
899
+
900
+ \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u30FC\u306F\u73FE\u72B6\u8AAD\u307F\u53D6\u308A\u5C02\u7528\u3067\u3059\u3002\u6B21\u306E\u3044\u305A\u308C\u304B\u3092\u6E21\u3057\u3066\u304F\u3060\u3055\u3044:
901
+
902
+ - \`https://www.googleapis.com/auth/calendar.readonly\` \u2014 \u30AB\u30EC\u30F3\u30C0\u30FC\u3068\u30A4\u30D9\u30F3\u30C8\u306E\u8AAD\u307F\u53D6\u308A
903
+ - \`https://www.googleapis.com/auth/calendar.events.readonly\` \u2014 \u30A4\u30D9\u30F3\u30C8\u306E\u307F\u8AAD\u307F\u53D6\u308A\uFF08\u30AB\u30EC\u30F3\u30C0\u30FC\u30E1\u30BF\u30C7\u30FC\u30BF\u4E0D\u53EF\uFF09
904
+ - \`https://www.googleapis.com/auth/calendar.freebusy\` \u2014 \u7A7A\u304D\u72B6\u6CC1\u30AF\u30A8\u30EA\u306E\u307F
905
+
906
+ \`request_with_delegation\` \u306E\u5834\u5408\u3001\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
907
+
908
+ \u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u5225\u306E\u6B63\u78BA\u306A scope \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9: https://developers.google.com/calendar/api/auth
909
+
910
+ ### \u9069\u5207\u306A\u30C4\u30FC\u30EB\u306E\u9078\u3073\u65B9
911
+
912
+ \u3053\u306E\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u3067\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u308B\u5404\u30AB\u30EC\u30F3\u30C0\u30FC\u306B\u306F\u30A2\u30AF\u30BB\u30B9\u7D4C\u8DEF\u304C\u3042\u308A\u307E\u3059\u3002\u7D4C\u8DEF\u306B\u5FDC\u3058\u3066\u30C4\u30FC\u30EB\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044:
913
+
914
+ - delegation \u7D4C\u7531\uFF08Workspace \u30E6\u30FC\u30B6\u30FC\u306E\u30AB\u30EC\u30F3\u30C0\u30FC\u306B Domain-wide Delegation \u3067\u30A2\u30AF\u30BB\u30B9\uFF09\u2192 \`request_with_delegation\` \u3092\u4F7F\u3044\u3001\u5BFE\u8C61\u30E6\u30FC\u30B6\u30FC\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092 \`subject\` \u3068\u3057\u3066\u6E21\u3059
915
+ - service account \u7D4C\u7531\uFF08\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u306B\u76F4\u63A5\u5171\u6709\u3055\u308C\u305F\u30AB\u30EC\u30F3\u30C0\u30FC\uFF09\u2192 \`request\` \u3092\u4F7F\u3046\uFF08\`subject\` \u4E0D\u8981\uFF09
916
+
917
+ ### \u30D1\u30B9\u306E\u66F8\u304D\u65B9
918
+
919
+ calendar ID \u3092\u30D1\u30B9\u306B\u76F4\u63A5\u66F8\u3044\u3066\u304F\u3060\u3055\u3044\u3002\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u306E\u7F6E\u63DB\u306F\u3042\u308A\u307E\u305B\u3093\u3002\u4F8B:
920
+
921
+ - \`/users/me/calendarList\` \u2014 \u8A8D\u8A3C\u3055\u308C\u305F\u8B58\u5225\u5B50\u304C\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u30AB\u30EC\u30F3\u30C0\u30FC\u4E00\u89A7
922
+ - \`/calendars/alice@example.com/events\` \u2014 alice \u306E\u30D7\u30E9\u30A4\u30DE\u30EA\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u30A4\u30D9\u30F3\u30C8
923
+ - \`/calendars/c_abc123@group.calendar.google.com/events\` \u2014 \u4E8C\u6B21\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u30A4\u30D9\u30F3\u30C8
869
924
 
870
925
  ### Google Calendar API v3 \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
871
926
 
872
927
  #### \u5229\u7528\u53EF\u80FD\u306A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8
873
928
  - GET \`/calendars/{calendarId}\` \u2014 \u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u53D6\u5F97
874
- - GET \`/users/me/calendarList\` \u2014 \u8A8D\u8A3C\u30E6\u30FC\u30B6\u30FC\u304C\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u5168\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u4E00\u89A7
929
+ - GET \`/users/me/calendarList\` \u2014 \u8A8D\u8A3C\u3055\u308C\u305F\u8B58\u5225\u5B50\u304C\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u5168\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u4E00\u89A7
875
930
  - GET \`/calendars/{calendarId}/events\` \u2014 \u30AB\u30EC\u30F3\u30C0\u30FC\u4E0A\u306E\u30A4\u30D9\u30F3\u30C8\u4E00\u89A7
876
931
  - GET \`/calendars/{calendarId}/events/{eventId}\` \u2014 \u5358\u4E00\u30A4\u30D9\u30F3\u30C8\u306E\u53D6\u5F97
877
932
 
@@ -883,12 +938,52 @@ export default async function handler(c: Context) {
883
938
  - \`orderBy=startTime\` \u2014 \u958B\u59CB\u6642\u9593\u9806\u306B\u4E26\u3079\u66FF\u3048\uFF08singleEvents=true\u304C\u5FC5\u8981\uFF09
884
939
  - \`q\` \u2014 \u30D5\u30EA\u30FC\u30C6\u30AD\u30B9\u30C8\u691C\u7D22\u8A9E
885
940
 
886
- #### \u30D2\u30F3\u30C8
887
- - \u30D1\u30B9\u306B \`{calendarId}\` \u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u3092\u4F7F\u7528 \u2014 \u8A2D\u5B9A\u6E08\u307F\u306E\u30C7\u30D5\u30A9\u30EB\u30C8\u30AB\u30EC\u30F3\u30C0\u30FCID\u3067\u81EA\u52D5\u7684\u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059
888
- - \`singleEvents=true\` \u3092\u8A2D\u5B9A\u3059\u308B\u3068\u3001\u7E70\u308A\u8FD4\u3057\u30A4\u30D9\u30F3\u30C8\u304C\u500B\u5225\u306E\u30A4\u30F3\u30B9\u30BF\u30F3\u30B9\u306B\u5C55\u958B\u3055\u308C\u307E\u3059
889
- - \`orderBy=startTime\` \u3092\u4F7F\u7528\u3059\u308B\u5834\u5408\u3001\`singleEvents=true\` \u3082\u8A2D\u5B9A\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059
890
- - \u6642\u9593\u30D1\u30E9\u30E1\u30FC\u30BF\u306B\u306FRFC3339\u5F62\u5F0F\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4F8B: "2024-01-15T09:00:00Z" \u3084 "2024-01-15T09:00:00+09:00"\uFF09
891
- - \u30C7\u30D5\u30A9\u30EB\u30C8\u30AB\u30EC\u30F3\u30C0\u30FCID\u306F\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u306A\u3044\u5834\u5408 "primary" \u304C\u4F7F\u7528\u3055\u308C\u307E\u3059`
941
+ ### Business Logic
942
+
943
+ \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u30CF\u30F3\u30C9\u30E9\u5185\u3067\u306F\u30B3\u30CD\u30AF\u30BFSDK\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u8A8D\u8A3C\u60C5\u5831\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
944
+
945
+ SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
946
+
947
+ - \`client.requestWithDelegation(path, { subject, scopes, init? })\` \u2014 Domain-wide Delegation \u3067\u6307\u5B9A Workspace \u30E6\u30FC\u30B6\u30FC\u306B\u4EE3\u308F\u3063\u3066 API \u3092\u547C\u3076
948
+ - \`client.request(path, { scopes, init? })\` \u2014 SA \u81EA\u8EAB\u3068\u3057\u3066 API \u3092\u547C\u3076\uFF08SA \u306B\u5171\u6709\u3055\u308C\u305F\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u307F\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\uFF09
949
+
950
+ \u4E21\u30E1\u30BD\u30C3\u30C9\u3068\u3082 \`scopes\` \u3092\u53D7\u3051\u53D6\u308A\u307E\u3059\u3002\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u306B\u5FC5\u8981\u306A\u6700\u5C0F scope \u3092\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u6A19\u6E96\u306E \`Response\` \u3092\u8FD4\u3059\u306E\u3067 \`response.json()\` \u3067\u30DC\u30C7\u30A3\u3092\u53D6\u5F97\u3057\u307E\u3059\u3002\u30D1\u30B9\u306E\u66F8\u304D\u65B9\u306F\u30C4\u30FC\u30EB\u3068\u540C\u3058\u3002
951
+
952
+ #### Example
953
+
954
+ \`\`\`ts
955
+ import type { Context } from "hono";
956
+ import { connection } from "@squadbase/vite-server/connectors/google-calendar";
957
+
958
+ const calendar = connection("<connectionId>");
959
+ const READ = ["https://www.googleapis.com/auth/calendar.readonly"];
960
+
961
+ export default async function handler(c: Context) {
962
+ const now = new Date().toISOString();
963
+ const qs = new URLSearchParams({
964
+ timeMin: now,
965
+ maxResults: "10",
966
+ singleEvents: "true",
967
+ orderBy: "startTime",
968
+ });
969
+
970
+ // alice@example.com \u306F delegation \u7D4C\u8DEF\u3067\u30A2\u30AF\u30BB\u30B9
971
+ const aliceRes = await calendar.requestWithDelegation(
972
+ \`/calendars/alice@example.com/events?\${qs}\`,
973
+ { subject: "alice@example.com", scopes: READ },
974
+ );
975
+ const alice = await aliceRes.json();
976
+
977
+ // team@example.com \u306F SA \u306B\u76F4\u63A5\u5171\u6709
978
+ const teamRes = await calendar.request(
979
+ \`/calendars/team@example.com/events?\${qs}\`,
980
+ { scopes: READ },
981
+ );
982
+ const team = await teamRes.json();
983
+
984
+ return c.json({ alice: alice.items, team: team.items });
985
+ }
986
+ \`\`\``
892
987
  },
893
988
  tools
894
989
  });