@squadbase/vite-server 0.1.8-dev.d378524 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +749 -617
- package/dist/cli/{sshcrypto-VVJOJ3LR.node → sshcrypto-P3UBA7BP.node} +0 -0
- package/dist/connectors/gmail.js +214 -348
- package/dist/connectors/google-calendar.js +477 -449
- package/dist/index.js +749 -617
- package/dist/main.js +749 -617
- package/dist/{sshcrypto-VVJOJ3LR.node → sshcrypto-P3UBA7BP.node} +0 -0
- package/dist/vite-plugin.js +749 -617
- package/package.json +1 -1
|
@@ -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
|
|
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
|
-
|
|
139
|
-
|
|
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
|
|
176
|
-
const
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
191
|
-
|
|
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:
|
|
101
|
+
`google-calendar: failed to obtain access token (subject=${subject})`
|
|
218
102
|
);
|
|
219
103
|
}
|
|
220
|
-
|
|
104
|
+
const headers = new Headers(init?.headers);
|
|
105
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
106
|
+
return fetch(buildUrl(path2), { ...init, headers });
|
|
221
107
|
},
|
|
222
|
-
async
|
|
223
|
-
const
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
120
|
+
"google-calendar: failed to obtain access token (no subject)"
|
|
230
121
|
);
|
|
231
122
|
}
|
|
232
|
-
|
|
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
|
}
|
|
@@ -381,97 +274,47 @@ var AUTH_TYPES = {
|
|
|
381
274
|
USER_PASSWORD: "user-password"
|
|
382
275
|
};
|
|
383
276
|
|
|
384
|
-
// ../connectors/src/connectors/google-calendar/tools/
|
|
385
|
-
import * as crypto2 from "crypto";
|
|
277
|
+
// ../connectors/src/connectors/google-calendar/tools/request.ts
|
|
386
278
|
import { z } from "zod";
|
|
387
|
-
var TOKEN_URL2 = "https://oauth2.googleapis.com/token";
|
|
388
279
|
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
280
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
391
|
-
function
|
|
392
|
-
const
|
|
393
|
-
return
|
|
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
|
-
);
|
|
435
|
-
}
|
|
436
|
-
const data = await response.json();
|
|
437
|
-
return data.access_token;
|
|
281
|
+
function decodeServiceAccount(keyJsonBase64) {
|
|
282
|
+
const decoded = Buffer.from(keyJsonBase64, "base64").toString("utf-8");
|
|
283
|
+
return JSON.parse(decoded);
|
|
438
284
|
}
|
|
439
285
|
var inputSchema = z.object({
|
|
440
286
|
toolUseIntent: z.string().optional().describe(
|
|
441
287
|
"Brief description of what you intend to accomplish with this tool call"
|
|
442
288
|
),
|
|
443
|
-
connectionId: z.string().describe("ID of the Google Calendar connection to use")
|
|
289
|
+
connectionId: z.string().describe("ID of the Google Calendar connection to use"),
|
|
290
|
+
method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).describe("HTTP method"),
|
|
291
|
+
path: z.string().describe(
|
|
292
|
+
"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."
|
|
293
|
+
),
|
|
294
|
+
scopes: z.array(z.string()).describe(
|
|
295
|
+
"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"
|
|
296
|
+
),
|
|
297
|
+
queryParams: z.record(z.string(), z.string()).optional().describe("Query parameters to append to the URL"),
|
|
298
|
+
body: z.record(z.string(), z.unknown()).optional().describe("JSON request body for POST/PUT/PATCH")
|
|
444
299
|
});
|
|
445
300
|
var outputSchema = z.discriminatedUnion("success", [
|
|
446
301
|
z.object({
|
|
447
302
|
success: z.literal(true),
|
|
448
|
-
|
|
449
|
-
|
|
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
|
-
)
|
|
303
|
+
status: z.number(),
|
|
304
|
+
data: z.record(z.string(), z.unknown())
|
|
463
305
|
}),
|
|
464
306
|
z.object({
|
|
465
307
|
success: z.literal(false),
|
|
466
|
-
error: z.string()
|
|
308
|
+
error: z.string(),
|
|
309
|
+
serviceAccountEmail: z.string().optional()
|
|
467
310
|
})
|
|
468
311
|
]);
|
|
469
|
-
var
|
|
470
|
-
name: "
|
|
471
|
-
description: "
|
|
312
|
+
var requestTool = new ConnectorTool({
|
|
313
|
+
name: "request",
|
|
314
|
+
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
315
|
inputSchema,
|
|
473
316
|
outputSchema,
|
|
474
|
-
async execute({ connectionId }, connections) {
|
|
317
|
+
async execute({ connectionId, method, path: path2, scopes, queryParams, body }, connections) {
|
|
475
318
|
const connection2 = connections.find((c) => c.id === connectionId);
|
|
476
319
|
if (!connection2) {
|
|
477
320
|
return {
|
|
@@ -479,144 +322,89 @@ var listCalendarsTool = new ConnectorTool({
|
|
|
479
322
|
error: `Connection ${connectionId} not found`
|
|
480
323
|
};
|
|
481
324
|
}
|
|
482
|
-
const
|
|
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
|
-
);
|
|
325
|
+
const keyJsonBase64 = parameters.serviceAccountKeyJsonBase64.getValue(connection2);
|
|
493
326
|
let serviceAccount;
|
|
494
327
|
try {
|
|
495
|
-
|
|
496
|
-
const decoded = Buffer.from(keyJsonBase64, "base64").toString("utf-8");
|
|
497
|
-
serviceAccount = JSON.parse(decoded);
|
|
328
|
+
serviceAccount = decodeServiceAccount(keyJsonBase64);
|
|
498
329
|
} catch (err) {
|
|
499
330
|
const msg = err instanceof Error ? err.message : String(err);
|
|
500
331
|
return {
|
|
501
332
|
success: false,
|
|
502
|
-
error: `
|
|
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"
|
|
333
|
+
error: `Failed to decode service account key: ${msg}`
|
|
509
334
|
};
|
|
510
335
|
}
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
336
|
+
const serviceAccountEmail = serviceAccount.client_email;
|
|
337
|
+
console.log(
|
|
338
|
+
`[connector-request] google-calendar/${connection2.name}: ${method} ${path2} (service account)`
|
|
339
|
+
);
|
|
340
|
+
try {
|
|
341
|
+
const { GoogleAuth } = await import("google-auth-library");
|
|
342
|
+
const auth = new GoogleAuth({
|
|
343
|
+
credentials: {
|
|
344
|
+
client_email: serviceAccount.client_email,
|
|
345
|
+
private_key: serviceAccount.private_key
|
|
346
|
+
},
|
|
347
|
+
scopes
|
|
348
|
+
});
|
|
349
|
+
const token = await auth.getAccessToken();
|
|
350
|
+
if (!token) {
|
|
351
|
+
return {
|
|
352
|
+
success: false,
|
|
353
|
+
error: "Failed to obtain access token",
|
|
354
|
+
serviceAccountEmail
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
let url = `${BASE_URL2}${path2.startsWith("/") ? "" : "/"}${path2}`;
|
|
358
|
+
if (queryParams) {
|
|
359
|
+
const searchParams = new URLSearchParams(queryParams);
|
|
360
|
+
url += `?${searchParams.toString()}`;
|
|
361
|
+
}
|
|
514
362
|
const controller = new AbortController();
|
|
515
363
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
516
364
|
try {
|
|
517
|
-
const
|
|
518
|
-
const response = await fetch(
|
|
519
|
-
method
|
|
520
|
-
headers: {
|
|
365
|
+
const hasBody = body != null && ["POST", "PUT", "PATCH"].includes(method);
|
|
366
|
+
const response = await fetch(url, {
|
|
367
|
+
method,
|
|
368
|
+
headers: {
|
|
369
|
+
Authorization: `Bearer ${token}`,
|
|
370
|
+
"Content-Type": "application/json"
|
|
371
|
+
},
|
|
372
|
+
body: hasBody ? JSON.stringify(body) : void 0,
|
|
521
373
|
signal: controller.signal
|
|
522
374
|
});
|
|
523
|
-
const data = await response.json();
|
|
375
|
+
const data = await response.json().catch(() => ({}));
|
|
524
376
|
if (!response.ok) {
|
|
525
377
|
const errorObj = data?.error;
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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
|
-
});
|
|
378
|
+
const errorMessage = errorObj?.message ?? (typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`);
|
|
379
|
+
return {
|
|
380
|
+
success: false,
|
|
381
|
+
error: errorMessage,
|
|
382
|
+
serviceAccountEmail
|
|
383
|
+
};
|
|
541
384
|
}
|
|
542
|
-
|
|
543
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
544
|
-
errors.push({ impersonateEmail: email, error: msg });
|
|
385
|
+
return { success: true, status: response.status, data };
|
|
545
386
|
} finally {
|
|
546
387
|
clearTimeout(timeout);
|
|
547
388
|
}
|
|
389
|
+
} catch (err) {
|
|
390
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
391
|
+
return {
|
|
392
|
+
success: false,
|
|
393
|
+
error: msg,
|
|
394
|
+
serviceAccountEmail
|
|
395
|
+
};
|
|
548
396
|
}
|
|
549
|
-
return {
|
|
550
|
-
success: true,
|
|
551
|
-
calendars: aggregated,
|
|
552
|
-
errors
|
|
553
|
-
};
|
|
554
|
-
}
|
|
555
|
-
});
|
|
556
|
-
|
|
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
397
|
}
|
|
614
398
|
});
|
|
615
399
|
|
|
616
|
-
// ../connectors/src/connectors/google-calendar/tools/request.ts
|
|
400
|
+
// ../connectors/src/connectors/google-calendar/tools/request-with-delegation.ts
|
|
617
401
|
import { z as z2 } from "zod";
|
|
618
402
|
var BASE_URL3 = "https://www.googleapis.com/calendar/v3";
|
|
619
403
|
var REQUEST_TIMEOUT_MS2 = 6e4;
|
|
404
|
+
function decodeServiceAccount2(keyJsonBase64) {
|
|
405
|
+
const decoded = Buffer.from(keyJsonBase64, "base64").toString("utf-8");
|
|
406
|
+
return JSON.parse(decoded);
|
|
407
|
+
}
|
|
620
408
|
var inputSchema2 = z2.object({
|
|
621
409
|
toolUseIntent: z2.string().optional().describe(
|
|
622
410
|
"Brief description of what you intend to accomplish with this tool call"
|
|
@@ -624,13 +412,18 @@ var inputSchema2 = z2.object({
|
|
|
624
412
|
connectionId: z2.string().describe("ID of the Google Calendar connection to use"),
|
|
625
413
|
method: z2.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).describe("HTTP method"),
|
|
626
414
|
path: z2.string().describe(
|
|
627
|
-
"API path appended to https://www.googleapis.com/calendar/v3 (e.g., '/calendars/
|
|
415
|
+
"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
416
|
),
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
417
|
+
subject: z2.string().describe(
|
|
418
|
+
"Email of the Workspace user to impersonate via Domain-wide Delegation. The token will be issued as this user."
|
|
419
|
+
),
|
|
420
|
+
scopes: z2.array(z2.string()).describe(
|
|
421
|
+
"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"
|
|
422
|
+
),
|
|
423
|
+
queryParams: z2.record(z2.string(), z2.string()).optional().describe(
|
|
424
|
+
"Query parameters to append to the URL (e.g., { timeMin: '2025-01-01T00:00:00Z', maxResults: '10' })"
|
|
425
|
+
),
|
|
426
|
+
body: z2.record(z2.string(), z2.unknown()).optional().describe("JSON request body for POST/PUT/PATCH")
|
|
634
427
|
});
|
|
635
428
|
var outputSchema2 = z2.discriminatedUnion("success", [
|
|
636
429
|
z2.object({
|
|
@@ -640,17 +433,16 @@ var outputSchema2 = z2.discriminatedUnion("success", [
|
|
|
640
433
|
}),
|
|
641
434
|
z2.object({
|
|
642
435
|
success: z2.literal(false),
|
|
643
|
-
error: z2.string()
|
|
436
|
+
error: z2.string(),
|
|
437
|
+
serviceAccountEmail: z2.string().optional()
|
|
644
438
|
})
|
|
645
439
|
]);
|
|
646
|
-
var
|
|
647
|
-
name: "
|
|
648
|
-
description: `
|
|
649
|
-
Authentication is handled automatically using a service account.
|
|
650
|
-
{calendarId} in the path is automatically replaced with the connection's default calendar ID.`,
|
|
440
|
+
var requestWithDelegationTool = new ConnectorTool({
|
|
441
|
+
name: "request_with_delegation",
|
|
442
|
+
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
443
|
inputSchema: inputSchema2,
|
|
652
444
|
outputSchema: outputSchema2,
|
|
653
|
-
async execute({ connectionId, method, path: path2, queryParams, body
|
|
445
|
+
async execute({ connectionId, method, path: path2, subject, scopes, queryParams, body }, connections) {
|
|
654
446
|
const connection2 = connections.find((c) => c.id === connectionId);
|
|
655
447
|
if (!connection2) {
|
|
656
448
|
return {
|
|
@@ -658,41 +450,40 @@ Authentication is handled automatically using a service account.
|
|
|
658
450
|
error: `Connection ${connectionId} not found`
|
|
659
451
|
};
|
|
660
452
|
}
|
|
453
|
+
const keyJsonBase64 = parameters.serviceAccountKeyJsonBase64.getValue(connection2);
|
|
454
|
+
let serviceAccount;
|
|
455
|
+
try {
|
|
456
|
+
serviceAccount = decodeServiceAccount2(keyJsonBase64);
|
|
457
|
+
} catch (err) {
|
|
458
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
459
|
+
return {
|
|
460
|
+
success: false,
|
|
461
|
+
error: `Failed to decode service account key: ${msg}`
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
const serviceAccountEmail = serviceAccount.client_email;
|
|
661
465
|
console.log(
|
|
662
|
-
`[connector-request] google-calendar/${connection2.name}: ${method} ${path2}`
|
|
466
|
+
`[connector-request] google-calendar/${connection2.name}: ${method} ${path2} subject=${subject}`
|
|
663
467
|
);
|
|
664
468
|
try {
|
|
665
469
|
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
470
|
const auth = new GoogleAuth({
|
|
680
|
-
credentials
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
clientOptions: { subject
|
|
471
|
+
credentials: {
|
|
472
|
+
client_email: serviceAccount.client_email,
|
|
473
|
+
private_key: serviceAccount.private_key
|
|
474
|
+
},
|
|
475
|
+
scopes,
|
|
476
|
+
clientOptions: { subject }
|
|
686
477
|
});
|
|
687
478
|
const token = await auth.getAccessToken();
|
|
688
479
|
if (!token) {
|
|
689
480
|
return {
|
|
690
481
|
success: false,
|
|
691
|
-
error: "Failed to obtain access token"
|
|
482
|
+
error: "Failed to obtain access token",
|
|
483
|
+
serviceAccountEmail
|
|
692
484
|
};
|
|
693
485
|
}
|
|
694
|
-
|
|
695
|
-
let url = `${BASE_URL3}${resolvedPath.startsWith("/") ? "" : "/"}${resolvedPath}`;
|
|
486
|
+
let url = `${BASE_URL3}${path2.startsWith("/") ? "" : "/"}${path2}`;
|
|
696
487
|
if (queryParams) {
|
|
697
488
|
const searchParams = new URLSearchParams(queryParams);
|
|
698
489
|
url += `?${searchParams.toString()}`;
|
|
@@ -700,28 +491,24 @@ Authentication is handled automatically using a service account.
|
|
|
700
491
|
const controller = new AbortController();
|
|
701
492
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
|
|
702
493
|
try {
|
|
494
|
+
const hasBody = body != null && ["POST", "PUT", "PATCH"].includes(method);
|
|
703
495
|
const response = await fetch(url, {
|
|
704
496
|
method,
|
|
705
497
|
headers: {
|
|
706
498
|
Authorization: `Bearer ${token}`,
|
|
707
499
|
"Content-Type": "application/json"
|
|
708
500
|
},
|
|
709
|
-
body:
|
|
501
|
+
body: hasBody ? JSON.stringify(body) : void 0,
|
|
710
502
|
signal: controller.signal
|
|
711
503
|
});
|
|
712
|
-
|
|
713
|
-
return {
|
|
714
|
-
success: true,
|
|
715
|
-
status: 204,
|
|
716
|
-
data: { message: "Deleted successfully" }
|
|
717
|
-
};
|
|
718
|
-
}
|
|
719
|
-
const data = await response.json();
|
|
504
|
+
const data = await response.json().catch(() => ({}));
|
|
720
505
|
if (!response.ok) {
|
|
721
506
|
const errorObj = data?.error;
|
|
507
|
+
const errorMessage = errorObj?.message ?? (typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`);
|
|
722
508
|
return {
|
|
723
509
|
success: false,
|
|
724
|
-
error:
|
|
510
|
+
error: errorMessage,
|
|
511
|
+
serviceAccountEmail
|
|
725
512
|
};
|
|
726
513
|
}
|
|
727
514
|
return { success: true, status: response.status, data };
|
|
@@ -730,13 +517,200 @@ Authentication is handled automatically using a service account.
|
|
|
730
517
|
}
|
|
731
518
|
} catch (err) {
|
|
732
519
|
const msg = err instanceof Error ? err.message : String(err);
|
|
733
|
-
return {
|
|
520
|
+
return {
|
|
521
|
+
success: false,
|
|
522
|
+
error: msg,
|
|
523
|
+
serviceAccountEmail
|
|
524
|
+
};
|
|
734
525
|
}
|
|
735
526
|
}
|
|
736
527
|
});
|
|
737
528
|
|
|
529
|
+
// ../connectors/src/connectors/google-calendar/setup.ts
|
|
530
|
+
var requestToolName = `google-calendar-service-account_${requestTool.name}`;
|
|
531
|
+
var requestWithDelegationToolName = `google-calendar-service-account_${requestWithDelegationTool.name}`;
|
|
532
|
+
var READONLY_SCOPES = '["https://www.googleapis.com/auth/calendar.readonly"]';
|
|
533
|
+
var SERVICE_ACCOUNT_KEY_PARAM_SLUG = parameters.serviceAccountKeyJsonBase64.slug;
|
|
534
|
+
var googleCalendarOnboarding = new ConnectorOnboarding({
|
|
535
|
+
connectionSetupInstructions: {
|
|
536
|
+
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\u767A\u898B\u30FB\u9078\u629E\u3057\u3066 Project Knowledge \u306B\u8A18\u9332\u3057\u307E\u3059\u3002
|
|
537
|
+
|
|
538
|
+
## \u30B9\u30C6\u30C3\u30D7 1: \u30A2\u30AF\u30BB\u30B9\u65B9\u6CD5\u3092\u9078\u629E
|
|
539
|
+
|
|
540
|
+
\`askUserQuestion\` \u3067\u6B21\u306E 3 \u629E\u3092\u63D0\u793A\u3059\u308B:
|
|
541
|
+
- \`type\`: \`"select"\`
|
|
542
|
+
- \`question\`: \u300C\u3069\u306E\u30AB\u30EC\u30F3\u30C0\u30FC\u306B\u30A2\u30AF\u30BB\u30B9\u3057\u307E\u3059\u304B\uFF1F\u300D
|
|
543
|
+
- \`options\`:
|
|
544
|
+
- \`{ 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" }\`
|
|
545
|
+
- \`{ 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" }\`
|
|
546
|
+
- \`{ label: "\u4E21\u65B9\u3092\u7D44\u307F\u5408\u308F\u305B\u308B", value: "both" }\`
|
|
547
|
+
|
|
548
|
+
## \u30B9\u30C6\u30C3\u30D7 2: \u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u7D4C\u8DEF\u306E\u767A\u898B\u3068\u9078\u629E (\`"service-account"\` \u307E\u305F\u306F \`"both"\` \u306E\u5834\u5408)
|
|
549
|
+
|
|
550
|
+
1. \`${requestToolName}\` \u3092\u547C\u3093\u3067\u30AB\u30EC\u30F3\u30C0\u30FC\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B:
|
|
551
|
+
- \`method\`: \`"GET"\`
|
|
552
|
+
- \`path\`: \`"/users/me/calendarList"\`
|
|
553
|
+
- \`scopes\`: \`${READONLY_SCOPES}\`
|
|
554
|
+
|
|
555
|
+
2. \u53D6\u5F97\u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
|
|
556
|
+
- \u7D50\u679C\u304C\u7A7A: \u30A2\u30AF\u30BB\u30B9\u65B9\u6CD5\u304C \`"service-account"\` \u3060\u3051\u306A\u3089\u30B9\u30C6\u30C3\u30D7 5 (\u30A8\u30B9\u30AB\u30EC\u30FC\u30B7\u30E7\u30F3) \u3078\u3002\`"both"\` \u306E\u5834\u5408\u306F\u305D\u306E\u307E\u307E\u30B9\u30C6\u30C3\u30D7 3 \u3078\u9032\u3080
|
|
557
|
+
- \u7D50\u679C\u304C\u3042\u308B: \`askUserQuestion\` \u3067\u30AB\u30EC\u30F3\u30C0\u30FC\u3092\u9078\u3070\u305B\u308B
|
|
558
|
+
- \`type\`: \`"multiSelect"\`
|
|
559
|
+
- \`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
|
|
560
|
+
- \`options\`: \u5404\u30AB\u30EC\u30F3\u30C0\u30FC\u306B\u3064\u3044\u3066 \`label\`: \`"<\u30AB\u30EC\u30F3\u30C0\u30FC\u540D>"\`\u3001\`value\`: \`"<calendarId>"\`
|
|
561
|
+
|
|
562
|
+
## \u30B9\u30C6\u30C3\u30D7 3: \u30C9\u30E1\u30A4\u30F3\u5168\u4F53\u306E\u59D4\u4EFB\u7D4C\u8DEF\u306E\u767A\u898B\u3068\u9078\u629E (\`"delegation"\` \u307E\u305F\u306F \`"both"\` \u306E\u5834\u5408)
|
|
563
|
+
|
|
564
|
+
1. \`askUserQuestion\` \u3067\u5BFE\u8C61\u30E6\u30FC\u30B6\u30FC\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u805E\u304F:
|
|
565
|
+
- \`type\`: \`"freeText"\`
|
|
566
|
+
- \`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\u3001\u3053\u306E\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u306B\u5BFE\u3057\u3066\u30C9\u30E1\u30A4\u30F3\u5168\u4F53\u306E\u59D4\u4EFB\u3092\u3042\u3089\u304B\u3058\u3081\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
|
|
567
|
+
- \`placeholder\`: \`"alice@example.com, bob@example.com"\`
|
|
568
|
+
|
|
569
|
+
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\u3057\u3001\u5404 \`<email>\` \u306B\u3064\u3044\u3066 \`${requestWithDelegationToolName}\` \u3092\u547C\u3076:
|
|
570
|
+
- \`method\`: \`"GET"\`
|
|
571
|
+
- \`path\`: \`"/users/me/calendarList"\`
|
|
572
|
+
- \`subject\`: \`<email>\`
|
|
573
|
+
- \`scopes\`: \`${READONLY_SCOPES}\`
|
|
574
|
+
|
|
575
|
+
3. \u53D6\u5F97\u7D50\u679C\u306B\u5FDC\u3058\u3066\u5206\u5C90:
|
|
576
|
+
- \u5168 email \u3067\u53D6\u5F97\u5931\u6557: \u30A2\u30AF\u30BB\u30B9\u65B9\u6CD5\u304C \`"delegation"\` \u3060\u3051\u3001\u3082\u3057\u304F\u306F \`"both"\` \u3067\u30B9\u30C6\u30C3\u30D7 2 \u3067\u3082 0 \u4EF6\u306E\u5834\u5408\u306F\u30B9\u30C6\u30C3\u30D7 5 \u3078\u3002\`"both"\` \u3067\u30B9\u30C6\u30C3\u30D7 2 \u306B\u30AB\u30EC\u30F3\u30C0\u30FC\u304C\u3042\u3063\u305F\u5834\u5408\u306F\u305D\u306E\u307E\u307E\u30B9\u30C6\u30C3\u30D7 4 \u3078
|
|
577
|
+
- \u4E00\u90E8\u307E\u305F\u306F\u5168\u4EF6\u6210\u529F: \`askUserQuestion\` \u3067\u30AB\u30EC\u30F3\u30C0\u30FC\u3092\u9078\u3070\u305B\u308B
|
|
578
|
+
- \`type\`: \`"multiSelect"\`
|
|
579
|
+
- \`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
|
|
580
|
+
- \`options\`: \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>"\`
|
|
581
|
+
- \u4E00\u90E8\u5931\u6557\u304C\u3042\u308C\u3070\u3001\u305D\u306E\u65E8\u3092 1 \u6587\u3067\u77ED\u304F\u4F1D\u3048\u308B
|
|
582
|
+
|
|
583
|
+
## \u30B9\u30C6\u30C3\u30D7 4: Project Knowledge \u306B\u8A18\u9332
|
|
584
|
+
|
|
585
|
+
\u30B9\u30C6\u30C3\u30D7 2 \u3068 3 \u3067\u9078\u3070\u308C\u305F calendarId \u96C6\u5408\u3092\u30C7\u30A3\u30B9\u30AB\u30D0\u30EA\u7D50\u679C\u3068\u7A81\u304D\u5408\u308F\u305B\u3066\u3001\u5404\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u7D4C\u8DEF\u3068 subject \u3092\u7279\u5B9A\u3059\u308B\u3002\`finalizeSetup\` \u3092\u547C\u3073\u3001\`projectKnowledge\` \u306E \`#### \u30B9\u30B3\u30FC\u30D7\` \u7BC0\u306B\u5404\u30AB\u30EC\u30F3\u30C0\u30FC\u3092 1 \u884C\u305A\u3064\u5217\u6319\u3059\u308B:
|
|
586
|
+
- \u30C9\u30E1\u30A4\u30F3\u5168\u4F53\u306E\u59D4\u4EFB\u7D4C\u7531: \`- calendar: <calendarId> (delegation, subject: <subject>, name: "<\u30AB\u30EC\u30F3\u30C0\u30FC\u540D>")\`
|
|
587
|
+
- \u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u7D4C\u8DEF: \`- calendar: <calendarId> (service-account, name: "<\u30AB\u30EC\u30F3\u30C0\u30FC\u540D>")\`
|
|
588
|
+
|
|
589
|
+
## \u30B9\u30C6\u30C3\u30D7 5: \u6700\u7D42\u7684\u306B\u30AB\u30EC\u30F3\u30C0\u30FC\u304C 0 \u4EF6\u306E\u5834\u5408\u306E\u30A8\u30B9\u30AB\u30EC\u30FC\u30B7\u30E7\u30F3
|
|
590
|
+
|
|
591
|
+
\u6700\u7D42\u7684\u306B\u9078\u629E\u30AB\u30EC\u30F3\u30C0\u30FC\u304C 1 \u4EF6\u3082\u7121\u3044\u5834\u5408 (\u5404\u7D4C\u8DEF\u306E\u767A\u898B\u304C\u7A7A\u3001\u307E\u305F\u306F\u9078\u629E\u3055\u308C\u306A\u304B\u3063\u305F)\u3001\u30A8\u30E9\u30FC\u30EC\u30B9\u30DD\u30F3\u30B9\u304B\u3089 \`serviceAccountEmail\` \u3092\u53D6\u308A\u51FA\u3057\u3001\`askUserQuestion\` \u3067\u6B21\u306E\u3088\u3046\u306B\u6848\u5185\u3059\u308B:
|
|
592
|
+
|
|
593
|
+
- \`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\u6B21\u306E\u3044\u305A\u308C\u304B\u3092\u884C\u3063\u3066\u304B\u3089\u7D9A\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8: \`<serviceAccountEmail>\`\u3002\u300D
|
|
594
|
+
- \`options\`: \u3053\u3053\u307E\u3067\u306B\u8A66\u3057\u305F\u7D4C\u8DEF\u306B\u5FDC\u3058\u3066\u4EE5\u4E0B\u3092\u7D44\u307F\u5408\u308F\u305B\u308B:
|
|
595
|
+
- \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" }\`
|
|
596
|
+
- \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" }\`
|
|
597
|
+
- \u5E38\u306B: \`{ label: "\u30A2\u30AF\u30BB\u30B9\u65B9\u6CD5\u3092\u5909\u66F4\u3059\u308B", value: "restart" }\`
|
|
598
|
+
- \u5E38\u306B: \`{ label: "\u5225\u306E\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u3067\u8A2D\u5B9A\u3057\u76F4\u3059", value: "change-service-account" }\`
|
|
599
|
+
|
|
600
|
+
\u9078\u629E\u7D50\u679C\u306B\u5FDC\u3058\u305F\u6319\u52D5:
|
|
601
|
+
- "retry": \u76F4\u524D\u306E\u7D4C\u8DEF\u3092\u518D probe (\u5177\u4F53\u7684\u306A\u3084\u308A\u76F4\u3057\u65B9\u306F\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u306B\u4EFB\u305B\u308B)
|
|
602
|
+
- "restart": \u30B9\u30C6\u30C3\u30D7 1 \u304B\u3089\u518D\u5B9F\u884C
|
|
603
|
+
- "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:
|
|
604
|
+
- \`parameterSlug\`: \`"${SERVICE_ACCOUNT_KEY_PARAM_SLUG}"\`
|
|
605
|
+
- \u5B8C\u4E86\u5F8C\u3001\u30B9\u30C6\u30C3\u30D7 1 \u304B\u3089\u518D\u5B9F\u884C
|
|
606
|
+
|
|
607
|
+
## \u5236\u7D04
|
|
608
|
+
|
|
609
|
+
- \u4E0A\u8A18\u4EE5\u5916\u306E API \u547C\u3073\u51FA\u3057\u3092 setup \u4E2D\u306B\u884C\u308F\u306A\u3044
|
|
610
|
+
- \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`,
|
|
611
|
+
en: `Set up the Google Calendar connection. Ask the user how they want to access calendars, discover and select the target calendars, and record them in Project Knowledge.
|
|
612
|
+
|
|
613
|
+
## Step 1: Choose the access method
|
|
614
|
+
|
|
615
|
+
Call \`askUserQuestion\`:
|
|
616
|
+
- \`type\`: \`"select"\`
|
|
617
|
+
- \`question\`: "Which calendars do you want to access?"
|
|
618
|
+
- \`options\`:
|
|
619
|
+
- \`{ label: "Calendars shared with this service account", value: "service-account" }\`
|
|
620
|
+
- \`{ label: "Calendars of users in your organization (via Domain-wide Delegation)", value: "delegation" }\`
|
|
621
|
+
- \`{ label: "Both", value: "both" }\`
|
|
622
|
+
|
|
623
|
+
## Step 2: Discover and select via the service-account path (when \`"service-account"\` or \`"both"\`)
|
|
624
|
+
|
|
625
|
+
1. Call \`${requestToolName}\`:
|
|
626
|
+
- \`method\`: \`"GET"\`
|
|
627
|
+
- \`path\`: \`"/users/me/calendarList"\`
|
|
628
|
+
- \`scopes\`: \`${READONLY_SCOPES}\`
|
|
629
|
+
|
|
630
|
+
2. Branch on the result:
|
|
631
|
+
- Empty: if the access method is \`"service-account"\` only, jump to Step 5. If \`"both"\`, continue to Step 3.
|
|
632
|
+
- Non-empty: ask the user to pick:
|
|
633
|
+
- \`type\`: \`"multiSelect"\`
|
|
634
|
+
- \`question\`: "Select the calendars to use (multiple allowed)"
|
|
635
|
+
- \`options\`: For each calendar, \`label\`: \`"<calendar name>"\`, \`value\`: \`"<calendarId>"\`
|
|
636
|
+
|
|
637
|
+
## Step 3: Discover and select via the Domain-wide Delegation path (when \`"delegation"\` or \`"both"\`)
|
|
638
|
+
|
|
639
|
+
1. Ask the user for target emails:
|
|
640
|
+
- \`type\`: \`"freeText"\`
|
|
641
|
+
- \`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 this service account in advance ([admin setup guide](https://support.google.com/a/answer/162106))."
|
|
642
|
+
- \`placeholder\`: \`"alice@example.com, bob@example.com"\`
|
|
643
|
+
|
|
644
|
+
2. Extract individual emails and, for each \`<email>\`, call \`${requestWithDelegationToolName}\`:
|
|
645
|
+
- \`method\`: \`"GET"\`
|
|
646
|
+
- \`path\`: \`"/users/me/calendarList"\`
|
|
647
|
+
- \`subject\`: \`<email>\`
|
|
648
|
+
- \`scopes\`: \`${READONLY_SCOPES}\`
|
|
649
|
+
|
|
650
|
+
3. Branch on the results:
|
|
651
|
+
- All emails failed: if the access method is \`"delegation"\` only, or it's \`"both"\` but Step 2 also produced 0 calendars, jump to Step 5. If \`"both"\` and Step 2 had calendars, proceed to Step 4.
|
|
652
|
+
- Any success: ask the user to pick:
|
|
653
|
+
- \`type\`: \`"multiSelect"\`
|
|
654
|
+
- \`question\`: "Select the calendars to use (multiple allowed)"
|
|
655
|
+
- \`options\`: For each calendar, \`label\`: \`"<calendar name> (owner: <the subject used>)"\`, \`value\`: \`"<calendarId>"\`
|
|
656
|
+
- If some emails failed, mention that briefly in one sentence.
|
|
657
|
+
|
|
658
|
+
## Step 4: Record in Project Knowledge
|
|
659
|
+
|
|
660
|
+
Aggregate the calendarIds selected in Steps 2 and 3, cross-reference the discovery results to recover each calendar's access path and subject, and call \`finalizeSetup\`. Under \`#### \u30B9\u30B3\u30FC\u30D7\` in \`projectKnowledge\`, list each calendar on its own line:
|
|
661
|
+
- Via Domain-wide Delegation: \`- calendar: <calendarId> (delegation, subject: <subject>, name: "<calendar name>")\`
|
|
662
|
+
- Via service account: \`- calendar: <calendarId> (service-account, name: "<calendar name>")\`
|
|
663
|
+
|
|
664
|
+
## Step 5: Escalation when zero calendars are selected
|
|
665
|
+
|
|
666
|
+
If the final selected calendar set is empty (every attempted path returned nothing, or the user picked nothing), take \`serviceAccountEmail\` from any failed tool response and call \`askUserQuestion\`:
|
|
667
|
+
|
|
668
|
+
- \`question\`: "No accessible calendars found. Please do one of the following before continuing. Service account: \`<serviceAccountEmail>\`."
|
|
669
|
+
- \`options\`: Combine these based on which paths were attempted:
|
|
670
|
+
- Service-account path attempted: \`{ label: "Shared the calendar with the service account \u2014 retry", value: "retry" }\`
|
|
671
|
+
- Domain-wide Delegation path attempted: \`{ label: "Authorized Domain-wide Delegation \u2014 retry", value: "retry" }\`
|
|
672
|
+
- Always: \`{ label: "Change the access method", value: "restart" }\`
|
|
673
|
+
- Always: \`{ label: "Use a different service account", value: "change-service-account" }\`
|
|
674
|
+
|
|
675
|
+
Behavior per selection:
|
|
676
|
+
- "retry": re-probe the path that was just attempted (leave the exact retry plan to the agent)
|
|
677
|
+
- "restart": re-run from Step 1
|
|
678
|
+
- "change-service-account": call \`updateConnectionParameters\` to have the user re-upload the service account JSON:
|
|
679
|
+
- \`parameterSlug\`: \`"${SERVICE_ACCOUNT_KEY_PARAM_SLUG}"\`
|
|
680
|
+
- After it completes, re-run from Step 1
|
|
681
|
+
|
|
682
|
+
## Constraints
|
|
683
|
+
|
|
684
|
+
- Do not call any other API endpoints during setup
|
|
685
|
+
- Write at most 1 sentence between tool calls`
|
|
686
|
+
},
|
|
687
|
+
dataOverviewInstructions: {
|
|
688
|
+
en: `For each calendar recorded under \`#### \u30B9\u30B3\u30FC\u30D7\`, fetch metadata and a small sample of upcoming events. The annotation on each line tells you which tool to use:
|
|
689
|
+
- \`(delegation, subject: <email>, ...)\` \u2192 \`${requestWithDelegationToolName}\` with \`subject: <email>\`
|
|
690
|
+
- \`(service-account, ...)\` \u2192 \`${requestToolName}\`
|
|
691
|
+
|
|
692
|
+
Pass \`scopes: ${READONLY_SCOPES}\` for every call.
|
|
693
|
+
|
|
694
|
+
For each calendar:
|
|
695
|
+
1. \`method=GET\`, \`path=/calendars/<id>\` to fetch metadata.
|
|
696
|
+
2. \`method=GET\`, \`path=/calendars/<id>/events\`, \`queryParams={ timeMin: <RFC3339 now>, maxResults: "10", singleEvents: "true", orderBy: "startTime" }\`.`,
|
|
697
|
+
ja: `\`#### \u30B9\u30B3\u30FC\u30D7\` \u306E\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\u5404\u884C\u306E\u6CE8\u91C8\u3067\u4F7F\u7528\u3059\u308B\u30C4\u30FC\u30EB\u3092\u9078\u3073\u307E\u3059:
|
|
698
|
+
- \`(delegation, subject: <email>, ...)\` \u2192 \`${requestWithDelegationToolName}\` \u3092 \`subject: <email>\` \u4ED8\u304D\u3067\u547C\u3076
|
|
699
|
+
- \`(service-account, ...)\` \u2192 \`${requestToolName}\` \u3092\u547C\u3076
|
|
700
|
+
|
|
701
|
+
\`scopes\` \u306F\u6BCE\u56DE \`${READONLY_SCOPES}\` \u3092\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
702
|
+
|
|
703
|
+
\u5404\u30AB\u30EC\u30F3\u30C0\u30FC\u306B\u3064\u3044\u3066:
|
|
704
|
+
1. \`method=GET\`\u3001\`path=/calendars/<id>\` \u3067\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u53D6\u5F97
|
|
705
|
+
2. \`method=GET\`\u3001\`path=/calendars/<id>/events\`\u3001\`queryParams={ timeMin: <RFC3339 \u306E\u73FE\u5728\u6642\u523B>, maxResults: "10", singleEvents: "true", orderBy: "startTime" }\``
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
|
|
738
709
|
// ../connectors/src/connectors/google-calendar/index.ts
|
|
739
|
-
var tools = {
|
|
710
|
+
var tools = {
|
|
711
|
+
request: requestTool,
|
|
712
|
+
request_with_delegation: requestWithDelegationTool
|
|
713
|
+
};
|
|
740
714
|
var googleCalendarConnector = new ConnectorPlugin({
|
|
741
715
|
slug: "google-calendar",
|
|
742
716
|
authType: AUTH_TYPES.SERVICE_ACCOUNT,
|
|
@@ -749,58 +723,45 @@ var googleCalendarConnector = new ConnectorPlugin({
|
|
|
749
723
|
systemPrompt: {
|
|
750
724
|
en: `### Tools
|
|
751
725
|
|
|
752
|
-
|
|
726
|
+
This connector exposes two request tools that correspond to the two ways a Service Account can authenticate against the Google Calendar API:
|
|
753
727
|
|
|
754
|
-
|
|
728
|
+
- \`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.
|
|
729
|
+
- \`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
730
|
|
|
756
|
-
|
|
731
|
+
Both tools require a \`scopes\` argument.
|
|
757
732
|
|
|
758
|
-
|
|
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
|
|
733
|
+
### OAuth Scopes (pass as \`scopes\` argument)
|
|
763
734
|
|
|
764
|
-
|
|
735
|
+
This connector is currently read-only. Pass one of:
|
|
765
736
|
|
|
766
|
-
|
|
737
|
+
- \`https://www.googleapis.com/auth/calendar.readonly\` \u2014 read-only on calendars and events
|
|
738
|
+
- \`https://www.googleapis.com/auth/calendar.events.readonly\` \u2014 read-only on events (no calendar metadata)
|
|
739
|
+
- \`https://www.googleapis.com/auth/calendar.freebusy\` \u2014 busy/free time queries only
|
|
767
740
|
|
|
768
|
-
|
|
769
|
-
const calendar = connection("<connectionId>", { subject: "other-user@example.com" });
|
|
770
|
-
\`\`\`
|
|
741
|
+
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
742
|
|
|
772
|
-
|
|
773
|
-
import type { Context } from "hono";
|
|
774
|
-
import { connection } from "@squadbase/vite-server/connectors/google-calendar";
|
|
743
|
+
Per-endpoint scope reference: https://developers.google.com/calendar/api/auth
|
|
775
744
|
|
|
776
|
-
|
|
745
|
+
### Choosing the right tool
|
|
777
746
|
|
|
778
|
-
|
|
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
|
-
});
|
|
747
|
+
Read \`#### \u30B9\u30B3\u30FC\u30D7\` in the project knowledge for this connection. Each calendar appears as a \`- calendar: <id> (...)\` line whose annotation tells you which tool to use:
|
|
786
748
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
\`\`\`
|
|
749
|
+
- \`(delegation, subject: <email>, name: "...")\` \u2192 use \`request_with_delegation\` and pass \`subject: <email>\`
|
|
750
|
+
- \`(service-account, name: "...")\` \u2192 use \`request\` (no \`subject\`)
|
|
751
|
+
|
|
752
|
+
### Path conventions
|
|
753
|
+
|
|
754
|
+
Write the calendar ID directly into the path \u2014 there is no placeholder substitution. Examples:
|
|
755
|
+
|
|
756
|
+
- \`/users/me/calendarList\` \u2014 list calendars accessible to the authenticated identity
|
|
757
|
+
- \`/calendars/alice@example.com/events\` \u2014 events on alice's primary calendar
|
|
758
|
+
- \`/calendars/c_abc123@group.calendar.google.com/events\` \u2014 events on a secondary calendar
|
|
798
759
|
|
|
799
760
|
### Google Calendar API v3 Reference
|
|
800
761
|
|
|
801
762
|
#### Available Endpoints
|
|
802
763
|
- GET \`/calendars/{calendarId}\` \u2014 Get calendar metadata
|
|
803
|
-
- GET \`/users/me/calendarList\` \u2014 List all calendars accessible by the authenticated
|
|
764
|
+
- GET \`/users/me/calendarList\` \u2014 List all calendars accessible by the authenticated identity
|
|
804
765
|
- GET \`/calendars/{calendarId}/events\` \u2014 List events on a calendar
|
|
805
766
|
- GET \`/calendars/{calendarId}/events/{eventId}\` \u2014 Get a single event
|
|
806
767
|
|
|
@@ -812,66 +773,93 @@ export default async function handler(c: Context) {
|
|
|
812
773
|
- \`orderBy=startTime\` \u2014 Order by start time (requires singleEvents=true)
|
|
813
774
|
- \`q\` \u2014 Free text search terms
|
|
814
775
|
|
|
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
776
|
### Business Logic
|
|
826
777
|
|
|
827
|
-
|
|
778
|
+
The business logic type for this connector is "typescript". Use the connector SDK in your handler. Do NOT read credentials from environment variables.
|
|
828
779
|
|
|
829
|
-
SDK
|
|
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
|
|
780
|
+
SDK methods (client created via \`connection(connectionId)\`):
|
|
834
781
|
|
|
835
|
-
|
|
782
|
+
- \`client.requestWithDelegation(path, { subject, scopes, init? })\` \u2014 call the API as the impersonated Workspace user via Domain-wide Delegation
|
|
783
|
+
- \`client.request(path, { scopes, init? })\` \u2014 call the API as the service account itself (only calendars shared with the SA email are accessible)
|
|
836
784
|
|
|
837
|
-
|
|
785
|
+
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
786
|
|
|
839
|
-
|
|
840
|
-
const calendar = connection("<connectionId>", { subject: "other-user@example.com" });
|
|
841
|
-
\`\`\`
|
|
787
|
+
#### Example
|
|
842
788
|
|
|
843
789
|
\`\`\`ts
|
|
844
790
|
import type { Context } from "hono";
|
|
845
791
|
import { connection } from "@squadbase/vite-server/connectors/google-calendar";
|
|
846
792
|
|
|
847
793
|
const calendar = connection("<connectionId>");
|
|
794
|
+
const READ = ["https://www.googleapis.com/auth/calendar.readonly"];
|
|
848
795
|
|
|
849
796
|
export default async function handler(c: Context) {
|
|
850
797
|
const now = new Date().toISOString();
|
|
851
|
-
const
|
|
798
|
+
const qs = new URLSearchParams({
|
|
852
799
|
timeMin: now,
|
|
853
|
-
maxResults: 10,
|
|
854
|
-
singleEvents: true,
|
|
800
|
+
maxResults: "10",
|
|
801
|
+
singleEvents: "true",
|
|
855
802
|
orderBy: "startTime",
|
|
856
803
|
});
|
|
857
804
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
805
|
+
// Project knowledge says: alice@example.com is reachable via delegation
|
|
806
|
+
const aliceRes = await calendar.requestWithDelegation(
|
|
807
|
+
\`/calendars/alice@example.com/events?\${qs}\`,
|
|
808
|
+
{ subject: "alice@example.com", scopes: READ },
|
|
809
|
+
);
|
|
810
|
+
const alice = await aliceRes.json();
|
|
811
|
+
|
|
812
|
+
// Project knowledge says: team@example.com is shared with the SA
|
|
813
|
+
const teamRes = await calendar.request(
|
|
814
|
+
\`/calendars/team@example.com/events?\${qs}\`,
|
|
815
|
+
{ scopes: READ },
|
|
866
816
|
);
|
|
817
|
+
const team = await teamRes.json();
|
|
818
|
+
|
|
819
|
+
return c.json({ alice: alice.items, team: team.items });
|
|
867
820
|
}
|
|
868
|
-
|
|
821
|
+
\`\`\``,
|
|
822
|
+
ja: `### \u30C4\u30FC\u30EB
|
|
823
|
+
|
|
824
|
+
\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:
|
|
825
|
+
|
|
826
|
+
- \`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
|
|
827
|
+
- \`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
|
|
828
|
+
|
|
829
|
+
\u4E21\u30C4\u30FC\u30EB\u3068\u3082 \`scopes\` \u5F15\u6570\u304C\u5FC5\u9808\u3067\u3059\u3002
|
|
830
|
+
|
|
831
|
+
### OAuth \u30B9\u30B3\u30FC\u30D7 (\`scopes\` \u5F15\u6570\u3067\u6E21\u3059)
|
|
832
|
+
|
|
833
|
+
\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:
|
|
834
|
+
|
|
835
|
+
- \`https://www.googleapis.com/auth/calendar.readonly\` \u2014 \u30AB\u30EC\u30F3\u30C0\u30FC\u3068\u30A4\u30D9\u30F3\u30C8\u306E\u8AAD\u307F\u53D6\u308A
|
|
836
|
+
- \`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
|
|
837
|
+
- \`https://www.googleapis.com/auth/calendar.freebusy\` \u2014 \u7A7A\u304D\u72B6\u6CC1\u30AF\u30A8\u30EA\u306E\u307F
|
|
838
|
+
|
|
839
|
+
\`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
|
|
840
|
+
|
|
841
|
+
\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
|
|
842
|
+
|
|
843
|
+
### \u9069\u5207\u306A\u30C4\u30FC\u30EB\u306E\u9078\u3073\u65B9
|
|
844
|
+
|
|
845
|
+
\u3053\u306E\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E Project Knowledge \u306E \`#### \u30B9\u30B3\u30FC\u30D7\` \u3092\u8AAD\u3093\u3067\u304F\u3060\u3055\u3044\u3002\u5404\u30AB\u30EC\u30F3\u30C0\u30FC\u306F \`- calendar: <id> (...)\` \u5F62\u5F0F\u306E\u884C\u3068\u3057\u3066\u8A18\u9332\u3055\u308C\u3066\u304A\u308A\u3001\u6CE8\u91C8\u304C\u3069\u3061\u3089\u306E\u30C4\u30FC\u30EB\u3092\u4F7F\u3046\u3079\u304D\u304B\u3092\u793A\u3057\u307E\u3059:
|
|
846
|
+
|
|
847
|
+
- \`(delegation, subject: <email>, name: "...")\` \u2192 \`request_with_delegation\` \u3092\u4F7F\u3044\u3001\`subject: <email>\` \u3092\u6E21\u3059
|
|
848
|
+
- \`(service-account, name: "...")\` \u2192 \`request\` \u3092\u4F7F\u3046\uFF08\`subject\` \u4E0D\u8981\uFF09
|
|
849
|
+
|
|
850
|
+
### \u30D1\u30B9\u306E\u66F8\u304D\u65B9
|
|
851
|
+
|
|
852
|
+
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:
|
|
853
|
+
|
|
854
|
+
- \`/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
|
|
855
|
+
- \`/calendars/alice@example.com/events\` \u2014 alice \u306E\u30D7\u30E9\u30A4\u30DE\u30EA\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u30A4\u30D9\u30F3\u30C8
|
|
856
|
+
- \`/calendars/c_abc123@group.calendar.google.com/events\` \u2014 \u4E8C\u6B21\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u30A4\u30D9\u30F3\u30C8
|
|
869
857
|
|
|
870
858
|
### Google Calendar API v3 \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
|
|
871
859
|
|
|
872
860
|
#### \u5229\u7528\u53EF\u80FD\u306A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8
|
|
873
861
|
- 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\
|
|
862
|
+
- 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
863
|
- GET \`/calendars/{calendarId}/events\` \u2014 \u30AB\u30EC\u30F3\u30C0\u30FC\u4E0A\u306E\u30A4\u30D9\u30F3\u30C8\u4E00\u89A7
|
|
876
864
|
- GET \`/calendars/{calendarId}/events/{eventId}\` \u2014 \u5358\u4E00\u30A4\u30D9\u30F3\u30C8\u306E\u53D6\u5F97
|
|
877
865
|
|
|
@@ -883,12 +871,52 @@ export default async function handler(c: Context) {
|
|
|
883
871
|
- \`orderBy=startTime\` \u2014 \u958B\u59CB\u6642\u9593\u9806\u306B\u4E26\u3079\u66FF\u3048\uFF08singleEvents=true\u304C\u5FC5\u8981\uFF09
|
|
884
872
|
- \`q\` \u2014 \u30D5\u30EA\u30FC\u30C6\u30AD\u30B9\u30C8\u691C\u7D22\u8A9E
|
|
885
873
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
874
|
+
### Business Logic
|
|
875
|
+
|
|
876
|
+
\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
|
|
877
|
+
|
|
878
|
+
SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
|
|
879
|
+
|
|
880
|
+
- \`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
|
|
881
|
+
- \`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
|
|
882
|
+
|
|
883
|
+
\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
|
|
884
|
+
|
|
885
|
+
#### Example
|
|
886
|
+
|
|
887
|
+
\`\`\`ts
|
|
888
|
+
import type { Context } from "hono";
|
|
889
|
+
import { connection } from "@squadbase/vite-server/connectors/google-calendar";
|
|
890
|
+
|
|
891
|
+
const calendar = connection("<connectionId>");
|
|
892
|
+
const READ = ["https://www.googleapis.com/auth/calendar.readonly"];
|
|
893
|
+
|
|
894
|
+
export default async function handler(c: Context) {
|
|
895
|
+
const now = new Date().toISOString();
|
|
896
|
+
const qs = new URLSearchParams({
|
|
897
|
+
timeMin: now,
|
|
898
|
+
maxResults: "10",
|
|
899
|
+
singleEvents: "true",
|
|
900
|
+
orderBy: "startTime",
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
// Project Knowledge: alice@example.com \u306F delegation \u7D4C\u8DEF\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD
|
|
904
|
+
const aliceRes = await calendar.requestWithDelegation(
|
|
905
|
+
\`/calendars/alice@example.com/events?\${qs}\`,
|
|
906
|
+
{ subject: "alice@example.com", scopes: READ },
|
|
907
|
+
);
|
|
908
|
+
const alice = await aliceRes.json();
|
|
909
|
+
|
|
910
|
+
// Project Knowledge: team@example.com \u306F SA \u306B\u5171\u6709\u3055\u308C\u3066\u3044\u308B
|
|
911
|
+
const teamRes = await calendar.request(
|
|
912
|
+
\`/calendars/team@example.com/events?\${qs}\`,
|
|
913
|
+
{ scopes: READ },
|
|
914
|
+
);
|
|
915
|
+
const team = await teamRes.json();
|
|
916
|
+
|
|
917
|
+
return c.json({ alice: alice.items, team: team.items });
|
|
918
|
+
}
|
|
919
|
+
\`\`\``
|
|
892
920
|
},
|
|
893
921
|
tools
|
|
894
922
|
});
|