@squadbase/vite-server 0.1.11-dev.0c78963 → 0.1.12-dev.a9ac647

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.
@@ -0,0 +1,929 @@
1
+ // ../connectors/src/connectors/outlook-oauth/sdk/index.ts
2
+ var BASE_URL = "https://graph.microsoft.com/v1.0";
3
+ function appendListMessagesOptions(qs, options) {
4
+ if (!options) return;
5
+ if (options.top != null) qs.set("$top", String(options.top));
6
+ if (options.skip != null) qs.set("$skip", String(options.skip));
7
+ if (options.filter) qs.set("$filter", options.filter);
8
+ if (options.orderBy) qs.set("$orderby", options.orderBy);
9
+ if (options.select && options.select.length > 0) {
10
+ qs.set("$select", options.select.join(","));
11
+ }
12
+ if (options.search) qs.set("$search", `"${options.search.replace(/"/g, '\\"')}"`);
13
+ }
14
+ function appendListEventsOptions(qs, options) {
15
+ if (!options) return;
16
+ if (options.top != null) qs.set("$top", String(options.top));
17
+ if (options.skip != null) qs.set("$skip", String(options.skip));
18
+ if (options.filter) qs.set("$filter", options.filter);
19
+ if (options.orderBy) qs.set("$orderby", options.orderBy);
20
+ if (options.select && options.select.length > 0) {
21
+ qs.set("$select", options.select.join(","));
22
+ }
23
+ }
24
+ function createClient(_params, fetchFn = fetch) {
25
+ function request(path2, init) {
26
+ const url = `${BASE_URL}${path2.startsWith("/") ? "" : "/"}${path2}`;
27
+ return fetchFn(url, init);
28
+ }
29
+ async function getJson(path2) {
30
+ const res = await request(path2);
31
+ if (!res.ok) {
32
+ const body = await res.text();
33
+ throw new Error(`outlook: GET ${path2} failed (${res.status}): ${body}`);
34
+ }
35
+ return await res.json();
36
+ }
37
+ return {
38
+ request,
39
+ // ---------- Mail ----------
40
+ async getProfile() {
41
+ return getJson("/me");
42
+ },
43
+ async listMailFolders() {
44
+ return getJson("/me/mailFolders");
45
+ },
46
+ async listMessages(options) {
47
+ const qs = new URLSearchParams();
48
+ appendListMessagesOptions(qs, options);
49
+ const query = qs.toString();
50
+ return getJson(
51
+ `/me/messages${query ? `?${query}` : ""}`
52
+ );
53
+ },
54
+ async listMessagesInFolder(folderId, options) {
55
+ const qs = new URLSearchParams();
56
+ appendListMessagesOptions(qs, options);
57
+ const query = qs.toString();
58
+ return getJson(
59
+ `/me/mailFolders/${encodeURIComponent(folderId)}/messages${query ? `?${query}` : ""}`
60
+ );
61
+ },
62
+ async getMessage(messageId, options) {
63
+ const qs = new URLSearchParams();
64
+ if (options?.select && options.select.length > 0) {
65
+ qs.set("$select", options.select.join(","));
66
+ }
67
+ const query = qs.toString();
68
+ return getJson(
69
+ `/me/messages/${encodeURIComponent(messageId)}${query ? `?${query}` : ""}`
70
+ );
71
+ },
72
+ async listConversation(conversationId, options) {
73
+ const qs = new URLSearchParams();
74
+ qs.set(
75
+ "$filter",
76
+ `conversationId eq '${conversationId.replace(/'/g, "''")}'`
77
+ );
78
+ if (!options?.orderBy) qs.set("$orderby", "receivedDateTime asc");
79
+ appendListMessagesOptions(
80
+ qs,
81
+ options ? { ...options, filter: void 0 } : void 0
82
+ );
83
+ return getJson(
84
+ `/me/messages?${qs.toString()}`
85
+ );
86
+ },
87
+ // ---------- Attachments ----------
88
+ async listAttachments(messageId) {
89
+ return getJson(
90
+ `/me/messages/${encodeURIComponent(messageId)}/attachments`
91
+ );
92
+ },
93
+ async getAttachment(messageId, attachmentId) {
94
+ return getJson(
95
+ `/me/messages/${encodeURIComponent(messageId)}/attachments/${encodeURIComponent(attachmentId)}`
96
+ );
97
+ },
98
+ // ---------- Calendar ----------
99
+ async listCalendars() {
100
+ return getJson("/me/calendars");
101
+ },
102
+ async listEvents(options) {
103
+ const qs = new URLSearchParams();
104
+ appendListEventsOptions(qs, options);
105
+ const query = qs.toString();
106
+ const basePath = options?.calendarId ? `/me/calendars/${encodeURIComponent(options.calendarId)}/events` : "/me/events";
107
+ return getJson(
108
+ `${basePath}${query ? `?${query}` : ""}`
109
+ );
110
+ },
111
+ async getEvent(eventId) {
112
+ return getJson(
113
+ `/me/events/${encodeURIComponent(eventId)}`
114
+ );
115
+ },
116
+ async listCalendarView(startDateTime, endDateTime, options) {
117
+ const qs = new URLSearchParams();
118
+ qs.set("startDateTime", startDateTime);
119
+ qs.set("endDateTime", endDateTime);
120
+ appendListEventsOptions(qs, options);
121
+ const basePath = options?.calendarId ? `/me/calendars/${encodeURIComponent(options.calendarId)}/calendarView` : "/me/calendarView";
122
+ return getJson(
123
+ `${basePath}?${qs.toString()}`
124
+ );
125
+ }
126
+ };
127
+ }
128
+
129
+ // ../connectors/src/connector-onboarding.ts
130
+ var ConnectorOnboarding = class {
131
+ /** Phase 1: Connection setup instructions (optional — some connectors don't need this) */
132
+ connectionSetupInstructions;
133
+ /** Phase 2: Data overview instructions */
134
+ dataOverviewInstructions;
135
+ constructor(config) {
136
+ this.connectionSetupInstructions = config.connectionSetupInstructions;
137
+ this.dataOverviewInstructions = config.dataOverviewInstructions;
138
+ }
139
+ getConnectionSetupPrompt(language) {
140
+ return this.connectionSetupInstructions?.[language] ?? null;
141
+ }
142
+ getDataOverviewInstructions(language) {
143
+ return this.dataOverviewInstructions[language];
144
+ }
145
+ };
146
+
147
+ // ../connectors/src/connector-tool.ts
148
+ var ConnectorTool = class {
149
+ name;
150
+ description;
151
+ inputSchema;
152
+ outputSchema;
153
+ _execute;
154
+ constructor(config) {
155
+ this.name = config.name;
156
+ this.description = config.description;
157
+ this.inputSchema = config.inputSchema;
158
+ this.outputSchema = config.outputSchema;
159
+ this._execute = config.execute;
160
+ }
161
+ createTool(connections, config) {
162
+ return {
163
+ description: this.description,
164
+ inputSchema: this.inputSchema,
165
+ outputSchema: this.outputSchema,
166
+ execute: (input) => this._execute(input, connections, config)
167
+ };
168
+ }
169
+ };
170
+
171
+ // ../connectors/src/connector-plugin.ts
172
+ var ConnectorPlugin = class _ConnectorPlugin {
173
+ slug;
174
+ authType;
175
+ name;
176
+ description;
177
+ iconUrl;
178
+ parameters;
179
+ releaseFlag;
180
+ proxyPolicy;
181
+ experimentalAttributes;
182
+ categories;
183
+ onboarding;
184
+ systemPrompt;
185
+ tools;
186
+ query;
187
+ checkConnection;
188
+ constructor(config) {
189
+ this.slug = config.slug;
190
+ this.authType = config.authType;
191
+ this.name = config.name;
192
+ this.description = config.description;
193
+ this.iconUrl = config.iconUrl;
194
+ this.parameters = config.parameters;
195
+ this.releaseFlag = config.releaseFlag;
196
+ this.proxyPolicy = config.proxyPolicy;
197
+ this.experimentalAttributes = config.experimentalAttributes;
198
+ this.categories = config.categories ?? [];
199
+ this.onboarding = config.onboarding;
200
+ this.systemPrompt = config.systemPrompt;
201
+ this.tools = config.tools;
202
+ this.query = config.query;
203
+ this.checkConnection = config.checkConnection;
204
+ }
205
+ get connectorKey() {
206
+ return _ConnectorPlugin.deriveKey(this.slug, this.authType);
207
+ }
208
+ /**
209
+ * Create tools for connections that belong to this connector.
210
+ * Filters connections by connectorKey internally.
211
+ * Returns tools keyed as `${connectorKey}_${toolName}`.
212
+ */
213
+ createTools(connections, config, opts) {
214
+ const myConnections = connections.filter(
215
+ (c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
216
+ );
217
+ const result = {};
218
+ for (const t of Object.values(this.tools)) {
219
+ const tool = t.createTool(myConnections, config);
220
+ const originalToModelOutput = tool.toModelOutput;
221
+ result[`${this.connectorKey}_${t.name}`] = {
222
+ ...tool,
223
+ toModelOutput: async (options) => {
224
+ if (!originalToModelOutput) {
225
+ return opts.truncateOutput(options.output);
226
+ }
227
+ const modelOutput = await originalToModelOutput(options);
228
+ if (modelOutput.type === "text" || modelOutput.type === "json") {
229
+ return opts.truncateOutput(modelOutput.value);
230
+ }
231
+ return modelOutput;
232
+ }
233
+ };
234
+ }
235
+ return result;
236
+ }
237
+ static deriveKey(slug, authType) {
238
+ if (authType) return `${slug}-${authType}`;
239
+ const LEGACY_NULL_AUTH_TYPE_MAP = {
240
+ // user-password
241
+ "postgresql": "user-password",
242
+ "mysql": "user-password",
243
+ "clickhouse": "user-password",
244
+ "kintone": "user-password",
245
+ "squadbase-db": "user-password",
246
+ // service-account
247
+ "snowflake": "service-account",
248
+ "bigquery": "service-account",
249
+ "google-analytics": "service-account",
250
+ "google-calendar": "service-account",
251
+ "aws-athena": "service-account",
252
+ "redshift": "service-account",
253
+ // api-key
254
+ "databricks": "api-key",
255
+ "dbt": "api-key",
256
+ "airtable": "api-key",
257
+ "openai": "api-key",
258
+ "gemini": "api-key",
259
+ "anthropic": "api-key",
260
+ "wix-store": "api-key"
261
+ };
262
+ const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
263
+ if (fallbackAuthType) return `${slug}-${fallbackAuthType}`;
264
+ return slug;
265
+ }
266
+ };
267
+
268
+ // ../connectors/src/auth-types.ts
269
+ var AUTH_TYPES = {
270
+ OAUTH: "oauth",
271
+ API_KEY: "api-key",
272
+ JWT: "jwt",
273
+ SERVICE_ACCOUNT: "service-account",
274
+ PAT: "pat",
275
+ USER_PASSWORD: "user-password"
276
+ };
277
+
278
+ // ../connectors/src/lib/normalize-path.ts
279
+ function normalizeRequestPath(path2, basePathSegment) {
280
+ let p = path2.trim();
281
+ if (!p.startsWith("/")) p = "/" + p;
282
+ if (p === basePathSegment || p.startsWith(basePathSegment + "/")) {
283
+ p = p.slice(basePathSegment.length) || "/";
284
+ }
285
+ return p;
286
+ }
287
+
288
+ // ../connectors/src/connectors/outlook-oauth/tools/request.ts
289
+ import { z } from "zod";
290
+ var BASE_HOST = "https://graph.microsoft.com";
291
+ var BASE_PATH_SEGMENT = "/v1.0";
292
+ var BASE_URL2 = `${BASE_HOST}${BASE_PATH_SEGMENT}`;
293
+ var REQUEST_TIMEOUT_MS = 6e4;
294
+ var cachedToken = null;
295
+ async function getProxyToken(config) {
296
+ if (cachedToken && cachedToken.expiresAt > Date.now() + 6e4) {
297
+ return cachedToken.token;
298
+ }
299
+ const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
300
+ const res = await fetch(url, {
301
+ method: "POST",
302
+ headers: {
303
+ "Content-Type": "application/json",
304
+ "x-api-key": config.appApiKey,
305
+ "project-id": config.projectId
306
+ },
307
+ body: JSON.stringify({
308
+ sandboxId: config.sandboxId,
309
+ issuedBy: "coding-agent"
310
+ })
311
+ });
312
+ if (!res.ok) {
313
+ const errorText = await res.text().catch(() => res.statusText);
314
+ throw new Error(
315
+ `Failed to get proxy token: HTTP ${res.status} ${errorText}`
316
+ );
317
+ }
318
+ const data = await res.json();
319
+ cachedToken = {
320
+ token: data.token,
321
+ expiresAt: new Date(data.expiresAt).getTime()
322
+ };
323
+ return data.token;
324
+ }
325
+ var inputSchema = z.object({
326
+ toolUseIntent: z.string().optional().describe(
327
+ "Brief description of what you intend to accomplish with this tool call"
328
+ ),
329
+ connectionId: z.string().describe("ID of the Outlook OAuth connection to use"),
330
+ method: z.enum(["GET"]).describe("HTTP method (read-only, GET only)"),
331
+ path: z.string().describe(
332
+ "Microsoft Graph path appended to https://graph.microsoft.com/v1.0. Covers Outlook Mail (e.g. '/me', '/me/messages', '/me/messages/{id}', '/me/mailFolders', '/me/messages/{id}/attachments') and Outlook Calendar (e.g. '/me/calendars', '/me/events', '/me/events/{id}', '/me/calendarView'). Use '/me' as the user prefix."
333
+ ),
334
+ queryParams: z.record(z.string(), z.string()).optional().describe(
335
+ `Query parameters to append. Microsoft Graph supports OData operators ($filter, $orderby, $top, $skip, $select, $expand, $search). Examples: { $top: '10', $select: 'subject,from,receivedDateTime' } for mail; { $filter: "conversationId eq '<id>'", $orderby: 'receivedDateTime asc' } for a thread; { startDateTime: '2025-01-15T00:00:00Z', endDateTime: '2025-01-16T00:00:00Z', $orderby: 'start/dateTime' } for calendarView.`
336
+ )
337
+ });
338
+ var outputSchema = z.discriminatedUnion("success", [
339
+ z.object({
340
+ success: z.literal(true),
341
+ status: z.number(),
342
+ data: z.record(z.string(), z.unknown())
343
+ }),
344
+ z.object({
345
+ success: z.literal(false),
346
+ error: z.string()
347
+ })
348
+ ]);
349
+ var requestTool = new ConnectorTool({
350
+ name: "request",
351
+ description: `Send authenticated GET requests to Microsoft Graph for Outlook Mail and Calendar.
352
+ Authentication is handled automatically via OAuth proxy (Microsoft Entra ID).
353
+ All paths are relative to https://graph.microsoft.com/v1.0. Use '/me' as the user prefix.
354
+ Covers mailbox (\`/me/messages\`, \`/me/mailFolders\`), threads (\`/me/messages?$filter=conversationId eq '<id>'\`), attachments (\`/me/messages/{id}/attachments\`), calendars (\`/me/calendars\`), and events (\`/me/events\`, \`/me/calendarView\` for expanded occurrences).
355
+ For full-text search use the \`$search\` query parameter (must be wrapped in double quotes); for filtering use \`$filter\` with OData syntax (e.g., \`receivedDateTime ge 2025-01-01T00:00:00Z\`).`,
356
+ inputSchema,
357
+ outputSchema,
358
+ async execute({ connectionId, method, path: path2, queryParams }, connections, config) {
359
+ const connection2 = connections.find((c) => c.id === connectionId);
360
+ if (!connection2) {
361
+ return {
362
+ success: false,
363
+ error: `Connection ${connectionId} not found`
364
+ };
365
+ }
366
+ console.log(
367
+ `[connector-request] outlook-oauth/${connection2.name}: ${method} ${path2}`
368
+ );
369
+ try {
370
+ const normalizedPath = normalizeRequestPath(path2, BASE_PATH_SEGMENT);
371
+ let url = `${BASE_URL2}${normalizedPath}`;
372
+ if (queryParams) {
373
+ const searchParams = new URLSearchParams(queryParams);
374
+ url += `?${searchParams.toString()}`;
375
+ }
376
+ const token = await getProxyToken(config.oauthProxy);
377
+ const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
378
+ const controller = new AbortController();
379
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
380
+ try {
381
+ const response = await fetch(proxyUrl, {
382
+ method: "POST",
383
+ headers: {
384
+ "Content-Type": "application/json",
385
+ Authorization: `Bearer ${token}`
386
+ },
387
+ body: JSON.stringify({
388
+ url,
389
+ method
390
+ }),
391
+ signal: controller.signal
392
+ });
393
+ const data = await response.json();
394
+ if (!response.ok) {
395
+ const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
396
+ return { success: false, error: errorMessage };
397
+ }
398
+ return { success: true, status: response.status, data };
399
+ } finally {
400
+ clearTimeout(timeout);
401
+ }
402
+ } catch (err) {
403
+ const msg = err instanceof Error ? err.message : String(err);
404
+ return { success: false, error: msg };
405
+ }
406
+ }
407
+ });
408
+
409
+ // ../connectors/src/connectors/outlook-oauth/setup.ts
410
+ var requestToolName = `outlook-oauth_${requestTool.name}`;
411
+ var outlookOnboarding = new ConnectorOnboarding({
412
+ connectionSetupInstructions: {
413
+ ja: `\u4EE5\u4E0B\u306E\u624B\u9806\u3067 Outlook \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
414
+
415
+ 1. \`${requestToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u8A8D\u8A3C\u6E08\u307F\u30E6\u30FC\u30B6\u30FC\u306E\u30D7\u30ED\u30D5\u30A3\u30FC\u30EB\u3092\u53D6\u5F97\u3059\u308B:
416
+ - \`method\`: \`"GET"\`
417
+ - \`path\`: \`"/me"\`
418
+ 2. \u30A8\u30E9\u30FC\u304C\u8FD4\u3055\u308C\u305F\u5834\u5408\u306F\u3001OAuth \u8A8D\u8A3C\u304C\u6B63\u3057\u304F\u5B8C\u4E86\u3057\u3066\u3044\u308B\u304B\u3092\u30E6\u30FC\u30B6\u30FC\u306B\u78BA\u8A8D\u3059\u308B\u3088\u3046\u4F1D\u3048\u308B
419
+ 3. \`${requestToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u30E1\u30FC\u30EB\u30D5\u30A9\u30EB\u30C0\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B:
420
+ - \`method\`: \`"GET"\`
421
+ - \`path\`: \`"/me/mailFolders"\`
422
+
423
+ #### \u5236\u7D04
424
+ - **\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u4E2D\u306B\u30E1\u30C3\u30BB\u30FC\u30B8\u672C\u6587\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3053\u3068**\u3002\u5B9F\u884C\u3057\u3066\u3088\u3044\u306E\u306F\u4E0A\u8A18\u306E\u30D7\u30ED\u30D5\u30A3\u30FC\u30EB\u53D6\u5F97\u3068\u30D5\u30A9\u30EB\u30C0\u4E00\u89A7\u53D6\u5F97\u306E\u307F
425
+ - \u30C4\u30FC\u30EB\u9593\u306F1\u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057`,
426
+ en: `Follow these steps to set up the Outlook connection.
427
+
428
+ 1. Call \`${requestToolName}\` to get the authenticated user's profile:
429
+ - \`method\`: \`"GET"\`
430
+ - \`path\`: \`"/me"\`
431
+ 2. If an error is returned, ask the user to verify that OAuth authentication completed correctly
432
+ 3. Call \`${requestToolName}\` to get the mail folder list:
433
+ - \`method\`: \`"GET"\`
434
+ - \`path\`: \`"/me/mailFolders"\`
435
+
436
+ #### Constraints
437
+ - **Do NOT read message bodies during setup**. Only the profile and mail-folder requests specified above are allowed
438
+ - Write only 1 sentence between tool calls, then immediately call the next tool`
439
+ },
440
+ dataOverviewInstructions: {
441
+ en: `Mail
442
+ 1. Call outlook-oauth_request with GET /me/mailFolders to list mail folders
443
+ 2. Call outlook-oauth_request with GET /me/messages?$top=5&$select=id,subject,from,receivedDateTime,conversationId to sample recent messages
444
+ 3. Call outlook-oauth_request with GET /me/messages/{id} for an interesting message to inspect the full payload
445
+ 4. Call outlook-oauth_request with GET /me/mailFolders/{folderId}/messages?$top=5 to drill into a specific folder
446
+ 5. For threading, call outlook-oauth_request with GET /me/messages?$filter=conversationId%20eq%20'<id>'&$orderby=receivedDateTime%20asc to pull every message in a conversation
447
+
448
+ Calendar
449
+ 6. Call outlook-oauth_request with GET /me/calendars to list calendars (default + shared)
450
+ 7. Call outlook-oauth_request with GET /me/calendarView?startDateTime=<startISO>&endDateTime=<endISO>&$top=5&$select=id,subject,start,end,attendees to sample upcoming occurrences (expands recurring events)
451
+ 8. Call outlook-oauth_request with GET /me/events/{eventId} for an interesting event to inspect attendees, body, and location`,
452
+ ja: `\u30E1\u30FC\u30EB
453
+ 1. outlook-oauth_request \u3067 GET /me/mailFolders \u3092\u547C\u3073\u51FA\u3057\u3001\u30E1\u30FC\u30EB\u30D5\u30A9\u30EB\u30C0\u4E00\u89A7\u3092\u53D6\u5F97
454
+ 2. outlook-oauth_request \u3067 GET /me/messages?$top=5&$select=id,subject,from,receivedDateTime,conversationId \u3092\u547C\u3073\u51FA\u3057\u3001\u6700\u65B0\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0
455
+ 3. \u8208\u5473\u306E\u3042\u308B\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u3064\u3044\u3066 outlook-oauth_request \u3067 GET /me/messages/{id} \u3092\u547C\u3073\u51FA\u3057\u3001\u30DA\u30A4\u30ED\u30FC\u30C9\u5168\u4F53\u3092\u78BA\u8A8D
456
+ 4. outlook-oauth_request \u3067 GET /me/mailFolders/{folderId}/messages?$top=5 \u3092\u547C\u3073\u51FA\u3057\u3001\u7279\u5B9A\u30D5\u30A9\u30EB\u30C0\u306E\u4E2D\u8EAB\u3092\u78BA\u8A8D
457
+ 5. \u30B9\u30EC\u30C3\u30C9\u3092\u8FFD\u3046\u5834\u5408\u306F outlook-oauth_request \u3067 GET /me/messages?$filter=conversationId%20eq%20'<id>'&$orderby=receivedDateTime%20asc \u3092\u547C\u3073\u51FA\u3057\u3001\u4F1A\u8A71\u306B\u542B\u307E\u308C\u308B\u5168\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u53D6\u5F97
458
+
459
+ \u30AB\u30EC\u30F3\u30C0\u30FC
460
+ 6. outlook-oauth_request \u3067 GET /me/calendars \u3092\u547C\u3073\u51FA\u3057\u3001\u30AB\u30EC\u30F3\u30C0\u30FC\u4E00\u89A7 (\u30C7\u30D5\u30A9\u30EB\u30C8 + \u5171\u6709) \u3092\u53D6\u5F97
461
+ 7. outlook-oauth_request \u3067 GET /me/calendarView?startDateTime=<startISO>&endDateTime=<endISO>&$top=5&$select=id,subject,start,end,attendees \u3092\u547C\u3073\u51FA\u3057\u3001\u76F4\u8FD1\u306E occurrence \u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0 (\u7E70\u308A\u8FD4\u3057\u30A4\u30D9\u30F3\u30C8\u3092\u5C55\u958B)
462
+ 8. \u8208\u5473\u306E\u3042\u308B\u30A4\u30D9\u30F3\u30C8\u306B\u3064\u3044\u3066 outlook-oauth_request \u3067 GET /me/events/{eventId} \u3092\u547C\u3073\u51FA\u3057\u3001\u53C2\u52A0\u8005\u30FB\u672C\u6587\u30FB\u5834\u6240\u3092\u78BA\u8A8D`
463
+ }
464
+ });
465
+
466
+ // ../connectors/src/connectors/outlook-oauth/parameters.ts
467
+ var parameters = {};
468
+
469
+ // ../connectors/src/connectors/outlook-oauth/index.ts
470
+ var tools = { request: requestTool };
471
+ var outlookOauthConnector = new ConnectorPlugin({
472
+ slug: "outlook",
473
+ authType: AUTH_TYPES.OAUTH,
474
+ name: "Outlook",
475
+ description: "Connect to Microsoft Outlook (Mail + Calendar) via Microsoft Graph using OAuth. Read-only access to the user's mailbox, mail folders, messages, attachments, calendars, and events.",
476
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/1J1FrRTYJjOh3CcSIqsz3I/6a467b4d926075ff99dc60820e0ae4b1/Microsoft_Outlook_Icon__2025%C3%A2__present_.svg",
477
+ parameters,
478
+ releaseFlag: { dev1: true, dev2: false, prod: false },
479
+ categories: ["productivity"],
480
+ onboarding: outlookOnboarding,
481
+ proxyPolicy: {
482
+ allowlist: [
483
+ {
484
+ host: "graph.microsoft.com",
485
+ methods: ["GET"]
486
+ }
487
+ ]
488
+ },
489
+ systemPrompt: {
490
+ en: `### Tools
491
+
492
+ - \`outlook-oauth_request\`: The only way to call Microsoft Graph for Outlook (read-only). Use it to fetch the user profile (\`/me\`), list mail folders (\`/me/mailFolders\`), read messages (\`/me/messages\`, \`/me/messages/{id}\`), fetch attachments (\`/me/messages/{id}/attachments\`), list calendars (\`/me/calendars\`), and read events (\`/me/events\`, \`/me/calendarView\`). Authentication is configured automatically via OAuth.
493
+
494
+ ### Microsoft Graph Reference (Outlook Mail)
495
+
496
+ #### Available Endpoints
497
+ - GET \`/me\` \u2014 Get the signed-in user's profile (displayName, mail, userPrincipalName)
498
+ - GET \`/me/mailFolders\` \u2014 List the user's mail folders (Inbox, SentItems, Drafts, etc.)
499
+ - GET \`/me/mailFolders/{folderId}\` \u2014 Get a specific mail folder
500
+ - GET \`/me/mailFolders/{folderId}/messages\` \u2014 List messages within a folder
501
+ - GET \`/me/messages\` \u2014 List messages across all folders
502
+ - GET \`/me/messages/{id}\` \u2014 Get a specific message with full body
503
+ - GET \`/me/messages/{id}/attachments\` \u2014 List attachments on a message
504
+ - GET \`/me/messages/{id}/attachments/{attachmentId}\` \u2014 Get a specific attachment
505
+
506
+ #### Well-Known Folder Names (usable in place of {folderId})
507
+ - \`inbox\`, \`sentitems\`, \`drafts\`, \`deleteditems\`, \`junkemail\`, \`outbox\`, \`archive\`
508
+
509
+ #### Conversations (Threads)
510
+ - Every message has a \`conversationId\`. Fetch all messages in a thread with: GET \`/me/messages?$filter=conversationId eq '<id>'&$orderby=receivedDateTime asc\` (Gmail-style \`getThread\` equivalent).
511
+
512
+ ### Microsoft Graph Reference (Outlook Calendar)
513
+
514
+ #### Available Endpoints
515
+ - GET \`/me/calendars\` \u2014 List the user's calendars (default + any shared/group calendars)
516
+ - GET \`/me/calendars/{calendarId}\` \u2014 Get a calendar's metadata
517
+ - GET \`/me/events\` \u2014 List events on the default calendar (recurring events are returned as series masters \u2014 use \`/me/calendarView\` to expand)
518
+ - GET \`/me/calendars/{calendarId}/events\` \u2014 List events on a specific calendar
519
+ - GET \`/me/events/{eventId}\` \u2014 Get a single event
520
+ - GET \`/me/calendarView?startDateTime=&endDateTime=\` \u2014 List event **occurrences** in a date range (expands recurring events). Equivalent to Google Calendar's \`events?singleEvents=true\`
521
+ - GET \`/me/calendars/{calendarId}/calendarView?startDateTime=&endDateTime=\` \u2014 Same, scoped to a specific calendar
522
+
523
+ #### Calendar Tips
524
+ - Use \`/me/calendarView\` when you want individual occurrences of recurring events; \`/me/events\` is closer to the raw stored series
525
+ - \`startDateTime\` / \`endDateTime\` must be ISO-8601 UTC, e.g. \`2025-01-15T00:00:00Z\`
526
+ - Combine with \`$select=subject,start,end,attendees,location\` and \`$orderby=start/dateTime\` to keep responses small and sorted
527
+
528
+ #### Key Query Parameters (OData, shared by Mail + Calendar)
529
+ - \`$top\` \u2014 Maximum number of results (default 10, max 1000)
530
+ - \`$skip\` \u2014 Skip the first N results (for paging)
531
+ - \`$select\` \u2014 Comma-separated list of properties (e.g. \`subject,from,receivedDateTime,bodyPreview\`)
532
+ - \`$orderby\` \u2014 Sort (e.g. \`receivedDateTime desc\`, \`start/dateTime asc\`)
533
+ - \`$filter\` \u2014 OData filter (e.g. \`receivedDateTime ge 2025-01-01T00:00:00Z\`, \`from/emailAddress/address eq 'a@b.com'\`, \`isRead eq false\`, \`conversationId eq '<id>'\`, \`start/dateTime ge '2025-01-15T00:00:00'\`)
534
+ - \`$search\` \u2014 Full-text search; must be wrapped in double quotes (e.g. \`$search="project status"\`). \`$search\` and \`$filter\` cannot be combined
535
+ - \`$expand\` \u2014 Expand related resources (e.g. \`attachments\`, \`instances\` for recurring events)
536
+ - \`$count=true\` \u2014 Return total count (requires \`ConsistencyLevel: eventual\` header on some endpoints)
537
+
538
+ #### Pagination
539
+ - Responses include \`@odata.nextLink\` when more results exist; call that URL (or its path) to get the next page
540
+
541
+ #### Tips
542
+ - Use \`/me\` for the signed-in user \u2014 never substitute another user ID
543
+ - Prefer \`$select\` to avoid huge payloads; the full message body can be MB-sized when HTML
544
+ - \`bodyPreview\` (first ~255 chars) is included by default and is cheap for triage
545
+ - Date/time values are ISO-8601; calendar event times also include a \`timeZone\` field
546
+
547
+ ### Business Logic
548
+
549
+ The business logic type for this connector is "typescript". Write handler code using the connector SDK shown below. Do NOT access credentials directly from environment variables and do NOT read \`INTERNAL_SQUADBASE_*\` env vars \u2014 the SDK takes care of OAuth.
550
+
551
+ SDK surface (client created via \`connection(connectionId)\`):
552
+
553
+ Mail:
554
+ - \`client.request(path, init?)\` \u2014 low-level authenticated fetch (path appended to \`https://graph.microsoft.com/v1.0\`)
555
+ - \`client.getProfile()\` \u2014 fetch the authenticated user's profile
556
+ - \`client.listMailFolders()\` \u2014 list all mail folders
557
+ - \`client.listMessages(options?)\` \u2014 list messages with optional \`top\`, \`skip\`, \`filter\`, \`orderBy\`, \`select\`, \`search\`
558
+ - \`client.listMessagesInFolder(folderId, options?)\` \u2014 list messages in a folder (well-known names like \`inbox\` work)
559
+ - \`client.getMessage(id, options?)\` \u2014 fetch a specific message
560
+ - \`client.listConversation(conversationId, options?)\` \u2014 fetch every message in a thread (Gmail-style \`getThread\` equivalent; orders oldest-first by default)
561
+
562
+ Attachments:
563
+ - \`client.listAttachments(messageId)\` \u2014 list attachments on a message
564
+ - \`client.getAttachment(messageId, attachmentId)\` \u2014 fetch a single attachment (file attachments include base64 \`contentBytes\`)
565
+
566
+ Calendar:
567
+ - \`client.listCalendars()\` \u2014 list calendars accessible to the user
568
+ - \`client.listEvents(options?)\` \u2014 list events on the default calendar (or pass \`{ calendarId }\` for a specific one). Recurring events are returned as series masters
569
+ - \`client.getEvent(id)\` \u2014 fetch a single event
570
+ - \`client.listCalendarView(startDateTime, endDateTime, options?)\` \u2014 list event **occurrences** in a date range (recurring events expanded). Equivalent to Google Calendar's \`events?singleEvents=true\`
571
+
572
+ If a handler test fails with \`Connection proxy is not configured\`, retry \u2014 the sandbox is still initializing. Do NOT abandon the SDK and construct OAuth proxy URLs manually.
573
+
574
+ #### Example
575
+
576
+ \`\`\`ts
577
+ import { connection } from "@squadbase/vite-server/connectors/outlook-oauth";
578
+
579
+ const outlook = connection("<connectionId>");
580
+
581
+ // Get user profile
582
+ const profile = await outlook.getProfile();
583
+ console.log(profile.displayName, profile.mail);
584
+
585
+ // List recent unread messages in Inbox
586
+ const messages = await outlook.listMessagesInFolder("inbox", {
587
+ top: 10,
588
+ filter: "isRead eq false",
589
+ orderBy: "receivedDateTime desc",
590
+ select: ["id", "subject", "from", "receivedDateTime", "bodyPreview", "conversationId"],
591
+ });
592
+ for (const msg of messages.value) {
593
+ console.log(msg.receivedDateTime, msg.from?.emailAddress.address, msg.subject);
594
+ }
595
+
596
+ // Pull a whole conversation
597
+ const first = messages.value[0];
598
+ if (first?.conversationId) {
599
+ const thread = await outlook.listConversation(first.conversationId);
600
+ thread.value.forEach(m => console.log(m.receivedDateTime, m.bodyPreview));
601
+ }
602
+
603
+ // Fetch attachments on a message that has them
604
+ if (first?.hasAttachments) {
605
+ const atts = await outlook.listAttachments(first.id);
606
+ for (const a of atts.value) {
607
+ const full = await outlook.getAttachment(first.id, a.id);
608
+ console.log(full.name, full.contentType, full.size);
609
+ // full.contentBytes is base64 for file attachments
610
+ }
611
+ }
612
+
613
+ // List today's calendar events (expanded occurrences)
614
+ const todayStart = new Date(); todayStart.setHours(0, 0, 0, 0);
615
+ const todayEnd = new Date(todayStart); todayEnd.setDate(todayEnd.getDate() + 1);
616
+ const events = await outlook.listCalendarView(
617
+ todayStart.toISOString(),
618
+ todayEnd.toISOString(),
619
+ { orderBy: "start/dateTime", select: ["id", "subject", "start", "end", "attendees"] },
620
+ );
621
+ events.value.forEach(e => console.log(e.start.dateTime, e.subject));
622
+ \`\`\``,
623
+ ja: `### \u30C4\u30FC\u30EB
624
+
625
+ - \`outlook-oauth_request\`: Outlook \u5411\u3051\u306E Microsoft Graph \u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\uFF08\u8AAD\u307F\u53D6\u308A\u5C02\u7528\uFF09\u3002\u30E6\u30FC\u30B6\u30FC\u30D7\u30ED\u30D5\u30A3\u30FC\u30EB (\`/me\`)\u3001\u30E1\u30FC\u30EB\u30D5\u30A9\u30EB\u30C0 (\`/me/mailFolders\`)\u3001\u30E1\u30C3\u30BB\u30FC\u30B8 (\`/me/messages\`)\u3001\u6DFB\u4ED8\u30D5\u30A1\u30A4\u30EB (\`/me/messages/{id}/attachments\`)\u3001\u30AB\u30EC\u30F3\u30C0\u30FC (\`/me/calendars\`)\u3001\u30A4\u30D9\u30F3\u30C8 (\`/me/events\`, \`/me/calendarView\`) \u306E\u53D6\u5F97\u306B\u4F7F\u3044\u307E\u3059\u3002OAuth \u7D4C\u7531\u3067\u8A8D\u8A3C\u306F\u81EA\u52D5\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002
626
+
627
+ ### Microsoft Graph \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9 (Outlook \u30E1\u30FC\u30EB)
628
+
629
+ #### \u5229\u7528\u53EF\u80FD\u306A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8
630
+ - GET \`/me\` \u2014 \u30B5\u30A4\u30F3\u30A4\u30F3\u30E6\u30FC\u30B6\u30FC\u306E\u30D7\u30ED\u30D5\u30A3\u30FC\u30EB (displayName, mail, userPrincipalName)
631
+ - GET \`/me/mailFolders\` \u2014 \u30E1\u30FC\u30EB\u30D5\u30A9\u30EB\u30C0\u4E00\u89A7 (Inbox, SentItems, Drafts \u306A\u3069)
632
+ - GET \`/me/mailFolders/{folderId}\` \u2014 \u7279\u5B9A\u30D5\u30A9\u30EB\u30C0\u306E\u53D6\u5F97
633
+ - GET \`/me/mailFolders/{folderId}/messages\` \u2014 \u30D5\u30A9\u30EB\u30C0\u5185\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u4E00\u89A7
634
+ - GET \`/me/messages\` \u2014 \u5168\u30D5\u30A9\u30EB\u30C0\u6A2A\u65AD\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u4E00\u89A7
635
+ - GET \`/me/messages/{id}\` \u2014 \u7279\u5B9A\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u672C\u6587\u8FBC\u307F\u3067\u53D6\u5F97
636
+ - GET \`/me/messages/{id}/attachments\` \u2014 \u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u6DFB\u4ED8\u30D5\u30A1\u30A4\u30EB\u4E00\u89A7
637
+ - GET \`/me/messages/{id}/attachments/{attachmentId}\` \u2014 \u6DFB\u4ED8\u30D5\u30A1\u30A4\u30EB\u5358\u4F53\u53D6\u5F97
638
+
639
+ #### Well-Known \u30D5\u30A9\u30EB\u30C0\u540D ({folderId} \u306E\u4EE3\u308F\u308A\u306B\u4F7F\u7528\u53EF\u80FD)
640
+ - \`inbox\`, \`sentitems\`, \`drafts\`, \`deleteditems\`, \`junkemail\`, \`outbox\`, \`archive\`
641
+
642
+ #### \u4F1A\u8A71 (\u30B9\u30EC\u30C3\u30C9)
643
+ - \u3059\u3079\u3066\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u306F \`conversationId\` \u3092\u6301\u3064\u3002\u30B9\u30EC\u30C3\u30C9\u5185\u306E\u5168\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u53D6\u5F97\u3059\u308B\u306B\u306F: GET \`/me/messages?$filter=conversationId eq '<id>'&$orderby=receivedDateTime asc\` (Gmail \u306E \`getThread\` \u76F8\u5F53)\u3002
644
+
645
+ ### Microsoft Graph \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9 (Outlook \u30AB\u30EC\u30F3\u30C0\u30FC)
646
+
647
+ #### \u5229\u7528\u53EF\u80FD\u306A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8
648
+ - GET \`/me/calendars\` \u2014 \u30E6\u30FC\u30B6\u30FC\u306E\u30AB\u30EC\u30F3\u30C0\u30FC\u4E00\u89A7 (\u30C7\u30D5\u30A9\u30EB\u30C8 + \u5171\u6709/\u30B0\u30EB\u30FC\u30D7\u30AB\u30EC\u30F3\u30C0\u30FC)
649
+ - GET \`/me/calendars/{calendarId}\` \u2014 \u30AB\u30EC\u30F3\u30C0\u30FC\u5358\u4F53\u53D6\u5F97
650
+ - GET \`/me/events\` \u2014 \u30C7\u30D5\u30A9\u30EB\u30C8\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u30A4\u30D9\u30F3\u30C8\u4E00\u89A7\uFF08\u7E70\u308A\u8FD4\u3057\u30A4\u30D9\u30F3\u30C8\u306F series master \u306E\u307E\u307E\u8FD4\u308B \u2014 \u5C55\u958B\u3057\u305F\u3044\u5834\u5408\u306F \`/me/calendarView\`\uFF09
651
+ - GET \`/me/calendars/{calendarId}/events\` \u2014 \u7279\u5B9A\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u30A4\u30D9\u30F3\u30C8\u4E00\u89A7
652
+ - GET \`/me/events/{eventId}\` \u2014 \u30A4\u30D9\u30F3\u30C8\u5358\u4F53\u53D6\u5F97
653
+ - GET \`/me/calendarView?startDateTime=&endDateTime=\` \u2014 \u65E5\u4ED8\u7BC4\u56F2\u5185\u306E\u30A4\u30D9\u30F3\u30C8 **occurrence** \u4E00\u89A7\uFF08\u7E70\u308A\u8FD4\u3057\u3092\u5C55\u958B\uFF09\u3002Google Calendar \u306E \`events?singleEvents=true\` \u76F8\u5F53
654
+ - GET \`/me/calendars/{calendarId}/calendarView?startDateTime=&endDateTime=\` \u2014 \u540C\u4E0A\u3001\u7279\u5B9A\u30AB\u30EC\u30F3\u30C0\u30FC\u7248
655
+
656
+ #### \u30AB\u30EC\u30F3\u30C0\u30FC Tips
657
+ - \u7E70\u308A\u8FD4\u3057\u30A4\u30D9\u30F3\u30C8\u306E\u500B\u5225 occurrence \u304C\u6B32\u3057\u3044\u5834\u5408\u306F \`/me/calendarView\`\u3001\u751F\u306E\u30B7\u30EA\u30FC\u30BA\u304C\u6B32\u3057\u3044\u5834\u5408\u306F \`/me/events\`
658
+ - \`startDateTime\` / \`endDateTime\` \u306F ISO-8601 UTC\u3001\u4F8B \`2025-01-15T00:00:00Z\`
659
+ - \`$select=subject,start,end,attendees,location\` \u3068 \`$orderby=start/dateTime\` \u3092\u7D44\u307F\u5408\u308F\u305B\u308B\u3068\u30DA\u30A4\u30ED\u30FC\u30C9\u6291\u5236 + \u30BD\u30FC\u30C8\u3067\u304D\u308B
660
+
661
+ #### \u4E3B\u8981\u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF (OData; Mail \u3068 Calendar \u3067\u5171\u901A)
662
+ - \`$top\` \u2014 \u6700\u5927\u7D50\u679C\u6570 (\u30C7\u30D5\u30A9\u30EB\u30C8 10\u3001\u6700\u5927 1000)
663
+ - \`$skip\` \u2014 \u5148\u982D N \u4EF6\u3092\u30B9\u30AD\u30C3\u30D7 (\u30DA\u30FC\u30B8\u30F3\u30B0)
664
+ - \`$select\` \u2014 \u53D6\u5F97\u3059\u308B\u30D7\u30ED\u30D1\u30C6\u30A3\u3092\u30AB\u30F3\u30DE\u533A\u5207\u308A (\u4F8B \`subject,from,receivedDateTime,bodyPreview\`)
665
+ - \`$orderby\` \u2014 \u30BD\u30FC\u30C8 (\u4F8B \`receivedDateTime desc\`\u3001\`start/dateTime asc\`)
666
+ - \`$filter\` \u2014 OData \u30D5\u30A3\u30EB\u30BF (\u4F8B \`receivedDateTime ge 2025-01-01T00:00:00Z\`\u3001\`from/emailAddress/address eq 'a@b.com'\`\u3001\`isRead eq false\`\u3001\`conversationId eq '<id>'\`\u3001\`start/dateTime ge '2025-01-15T00:00:00'\`)
667
+ - \`$search\` \u2014 \u5168\u6587\u691C\u7D22\u3002\u30C0\u30D6\u30EB\u30AF\u30A9\u30FC\u30C8\u3067\u56F2\u3080\u5FC5\u8981\u3042\u308A (\u4F8B \`$search="project status"\`)\u3002\`$filter\` \u3068\u4F75\u7528\u4E0D\u53EF
668
+ - \`$expand\` \u2014 \u95A2\u9023\u30EA\u30BD\u30FC\u30B9\u3092\u5C55\u958B (\u4F8B \`attachments\`\u3001\u7E70\u308A\u8FD4\u3057\u30A4\u30D9\u30F3\u30C8\u306E \`instances\`)
669
+ - \`$count=true\` \u2014 \u7DCF\u4EF6\u6570\u3092\u8FD4\u3059 (\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u306B\u3088\u3063\u3066\u306F \`ConsistencyLevel: eventual\` \u30D8\u30C3\u30C0\u30FC\u304C\u5FC5\u8981)
670
+
671
+ #### \u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3
672
+ - \u7D9A\u304D\u304C\u3042\u308B\u5834\u5408\u30EC\u30B9\u30DD\u30F3\u30B9\u306B \`@odata.nextLink\` \u304C\u542B\u307E\u308C\u308B\u3002\u305D\u306E URL (\u307E\u305F\u306F\u30D1\u30B9) \u3092\u547C\u3073\u51FA\u3057\u3066\u6B21\u30DA\u30FC\u30B8\u3092\u53D6\u5F97
673
+
674
+ #### Tips
675
+ - \u30B5\u30A4\u30F3\u30A4\u30F3\u30E6\u30FC\u30B6\u30FC\u306B\u306F\u5FC5\u305A \`/me\` \u3092\u4F7F\u7528 \u2014 \u4ED6\u30E6\u30FC\u30B6\u30FC\u306E ID \u3092\u5165\u308C\u306A\u3044
676
+ - \u30DA\u30A4\u30ED\u30FC\u30C9\u80A5\u5927\u5316\u3092\u907F\u3051\u308B\u305F\u3081 \`$select\` \u3092\u6D3B\u7528\u3002HTML \u672C\u6587\u8FBC\u307F\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u306F MB \u5358\u4F4D\u306B\u306A\u308B\u3053\u3068\u304C\u3042\u308B
677
+ - \`bodyPreview\` (\u5148\u982D\u7D04 255 \u6587\u5B57) \u306F\u30C7\u30D5\u30A9\u30EB\u30C8\u3067\u542B\u307E\u308C\u3066\u304A\u308A\u3001\u30C8\u30EA\u30A2\u30FC\u30B8\u306B\u4FBF\u5229
678
+ - \u65E5\u6642\u5024\u306F ISO-8601\u3002\u30AB\u30EC\u30F3\u30C0\u30FC\u30A4\u30D9\u30F3\u30C8\u306B\u306F \`timeZone\` \u30D5\u30A3\u30FC\u30EB\u30C9\u3082\u542B\u307E\u308C\u308B
679
+
680
+ ### Business Logic
681
+
682
+ \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u4EE5\u4E0B\u306B\u793A\u3059\u30B3\u30CD\u30AF\u30BF SDK \u3092\u4F7F\u7528\u3057\u3066\u30CF\u30F3\u30C9\u30E9\u30B3\u30FC\u30C9\u3092\u8A18\u8FF0\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u76F4\u63A5\u8A8D\u8A3C\u60C5\u5831\u306B\u30A2\u30AF\u30BB\u30B9\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002\`INTERNAL_SQUADBASE_*\` \u306E\u74B0\u5883\u5909\u6570\u3092\u4F7F\u3063\u3066\u624B\u52D5\u3067 OAuth \u30D7\u30ED\u30AD\u30B7\u3092\u53E9\u304F\u3053\u3068\u3082\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044 \u2014 SDK \u304C OAuth \u3092\u51E6\u7406\u3057\u307E\u3059\u3002
683
+
684
+ SDK (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
685
+
686
+ \u30E1\u30FC\u30EB:
687
+ - \`client.request(path, init?)\` \u2014 \u4F4E\u30EC\u30D9\u30EB\u8A8D\u8A3C\u4ED8\u304D fetch (path \u306F \`https://graph.microsoft.com/v1.0\` \u306B\u8FFD\u52A0\u3055\u308C\u307E\u3059)
688
+ - \`client.getProfile()\` \u2014 \u8A8D\u8A3C\u30E6\u30FC\u30B6\u30FC\u306E\u30D7\u30ED\u30D5\u30A3\u30FC\u30EB\u3092\u53D6\u5F97
689
+ - \`client.listMailFolders()\` \u2014 \u5168\u30E1\u30FC\u30EB\u30D5\u30A9\u30EB\u30C0\u3092\u4E00\u89A7
690
+ - \`client.listMessages(options?)\` \u2014 \u30E1\u30C3\u30BB\u30FC\u30B8\u4E00\u89A7 (\`top\`, \`skip\`, \`filter\`, \`orderBy\`, \`select\`, \`search\` \u5BFE\u5FDC)
691
+ - \`client.listMessagesInFolder(folderId, options?)\` \u2014 \u30D5\u30A9\u30EB\u30C0\u5185\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u4E00\u89A7 (\`inbox\` \u306A\u3069\u306E well-known \u540D\u3082\u4F7F\u7528\u53EF)
692
+ - \`client.getMessage(id, options?)\` \u2014 \u7279\u5B9A\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u53D6\u5F97
693
+ - \`client.listConversation(conversationId, options?)\` \u2014 \u30B9\u30EC\u30C3\u30C9\u5185\u306E\u5168\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u53D6\u5F97 (Gmail \`getThread\` \u76F8\u5F53\u3001\u30C7\u30D5\u30A9\u30EB\u30C8\u3067\u53E4\u3044\u9806)
694
+
695
+ \u6DFB\u4ED8\u30D5\u30A1\u30A4\u30EB:
696
+ - \`client.listAttachments(messageId)\` \u2014 \u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u6DFB\u4ED8\u30D5\u30A1\u30A4\u30EB\u4E00\u89A7
697
+ - \`client.getAttachment(messageId, attachmentId)\` \u2014 \u6DFB\u4ED8\u30D5\u30A1\u30A4\u30EB\u5358\u4F53\u53D6\u5F97 (file attachment \u306F base64 \u306E \`contentBytes\` \u3092\u542B\u3080)
698
+
699
+ \u30AB\u30EC\u30F3\u30C0\u30FC:
700
+ - \`client.listCalendars()\` \u2014 \u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u30AB\u30EC\u30F3\u30C0\u30FC\u4E00\u89A7
701
+ - \`client.listEvents(options?)\` \u2014 \u30C7\u30D5\u30A9\u30EB\u30C8\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u30A4\u30D9\u30F3\u30C8\u4E00\u89A7 (\`{ calendarId }\` \u3067\u7279\u5B9A\u30AB\u30EC\u30F3\u30C0\u30FC\u6307\u5B9A\u53EF)\u3002\u7E70\u308A\u8FD4\u3057\u30A4\u30D9\u30F3\u30C8\u306F series master \u3068\u3057\u3066\u8FD4\u5374
702
+ - \`client.getEvent(id)\` \u2014 \u30A4\u30D9\u30F3\u30C8\u5358\u4F53\u53D6\u5F97
703
+ - \`client.listCalendarView(startDateTime, endDateTime, options?)\` \u2014 \u65E5\u4ED8\u7BC4\u56F2\u5185\u306E\u30A4\u30D9\u30F3\u30C8 **occurrence** \u4E00\u89A7 (\u7E70\u308A\u8FD4\u3057\u3092\u5C55\u958B)\u3002Google Calendar \u306E \`events?singleEvents=true\` \u76F8\u5F53
704
+
705
+ \u30CF\u30F3\u30C9\u30E9\u306E\u30C6\u30B9\u30C8\u304C \`Connection proxy is not configured\` \u3067\u5931\u6557\u3059\u308B\u5834\u5408\u306F\u518D\u8A66\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002SDK \u3092\u8AE6\u3081\u3066 OAuth \u30D7\u30ED\u30AD\u30B7\u306E URL \u3092\u81EA\u5206\u3067\u7D44\u307F\u7ACB\u3066\u308B\u3053\u3068\u306F **\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044**\u3002
706
+
707
+ #### Example
708
+
709
+ \`\`\`ts
710
+ import { connection } from "@squadbase/vite-server/connectors/outlook-oauth";
711
+
712
+ const outlook = connection("<connectionId>");
713
+
714
+ // \u30D7\u30ED\u30D5\u30A3\u30FC\u30EB\u53D6\u5F97
715
+ const profile = await outlook.getProfile();
716
+ console.log(profile.displayName, profile.mail);
717
+
718
+ // \u53D7\u4FE1\u30C8\u30EC\u30A4\u306E\u672A\u8AAD\u30E1\u30C3\u30BB\u30FC\u30B8\u6700\u65B010\u4EF6
719
+ const messages = await outlook.listMessagesInFolder("inbox", {
720
+ top: 10,
721
+ filter: "isRead eq false",
722
+ orderBy: "receivedDateTime desc",
723
+ select: ["id", "subject", "from", "receivedDateTime", "bodyPreview", "conversationId"],
724
+ });
725
+ for (const msg of messages.value) {
726
+ console.log(msg.receivedDateTime, msg.from?.emailAddress.address, msg.subject);
727
+ }
728
+
729
+ // \u30B9\u30EC\u30C3\u30C9\u5168\u4F53\u3092\u53D6\u5F97
730
+ const first = messages.value[0];
731
+ if (first?.conversationId) {
732
+ const thread = await outlook.listConversation(first.conversationId);
733
+ thread.value.forEach(m => console.log(m.receivedDateTime, m.bodyPreview));
734
+ }
735
+
736
+ // \u6DFB\u4ED8\u30D5\u30A1\u30A4\u30EB\u3092\u53D6\u5F97
737
+ if (first?.hasAttachments) {
738
+ const atts = await outlook.listAttachments(first.id);
739
+ for (const a of atts.value) {
740
+ const full = await outlook.getAttachment(first.id, a.id);
741
+ console.log(full.name, full.contentType, full.size);
742
+ // full.contentBytes \u306F file attachment \u306E\u5834\u5408 base64
743
+ }
744
+ }
745
+
746
+ // \u4ECA\u65E5\u306E\u30AB\u30EC\u30F3\u30C0\u30FC\u30A4\u30D9\u30F3\u30C8 (occurrence \u5C55\u958B)
747
+ const todayStart = new Date(); todayStart.setHours(0, 0, 0, 0);
748
+ const todayEnd = new Date(todayStart); todayEnd.setDate(todayEnd.getDate() + 1);
749
+ const events = await outlook.listCalendarView(
750
+ todayStart.toISOString(),
751
+ todayEnd.toISOString(),
752
+ { orderBy: "start/dateTime", select: ["id", "subject", "start", "end", "attendees"] },
753
+ );
754
+ events.value.forEach(e => console.log(e.start.dateTime, e.subject));
755
+ \`\`\``
756
+ },
757
+ tools,
758
+ async checkConnection(_params, config) {
759
+ const { proxyFetch } = config;
760
+ const url = "https://graph.microsoft.com/v1.0/me";
761
+ try {
762
+ const res = await proxyFetch(url, { method: "GET" });
763
+ if (!res.ok) {
764
+ const errorText = await res.text().catch(() => res.statusText);
765
+ return {
766
+ success: false,
767
+ error: `Microsoft Graph failed: HTTP ${res.status} ${errorText}`
768
+ };
769
+ }
770
+ return { success: true };
771
+ } catch (error) {
772
+ return {
773
+ success: false,
774
+ error: error instanceof Error ? error.message : String(error)
775
+ };
776
+ }
777
+ }
778
+ });
779
+
780
+ // src/connectors/create-connector-sdk.ts
781
+ import { readFileSync } from "fs";
782
+ import path from "path";
783
+
784
+ // src/connector-client/env.ts
785
+ function resolveEnvVar(entry, key, connectionId) {
786
+ const envVarName = entry.envVars[key];
787
+ if (!envVarName) {
788
+ throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
789
+ }
790
+ const value = process.env[envVarName];
791
+ if (!value) {
792
+ throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
793
+ }
794
+ return value;
795
+ }
796
+ function resolveEnvVarOptional(entry, key) {
797
+ const envVarName = entry.envVars[key];
798
+ if (!envVarName) return void 0;
799
+ return process.env[envVarName] || void 0;
800
+ }
801
+
802
+ // src/connector-client/proxy-fetch.ts
803
+ import { getContext } from "hono/context-storage";
804
+ import { getCookie } from "hono/cookie";
805
+ var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
806
+ function normalizeHeaders(input) {
807
+ const out = {};
808
+ if (!input) return out;
809
+ new Headers(input).forEach((value, key) => {
810
+ out[key] = value;
811
+ });
812
+ return out;
813
+ }
814
+ function createSandboxProxyFetch(connectionId) {
815
+ return async (input, init) => {
816
+ const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
817
+ const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
818
+ if (!token || !sandboxId) {
819
+ throw new Error(
820
+ "Connection proxy is not configured. Please check your deployment settings."
821
+ );
822
+ }
823
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
824
+ const originalMethod = init?.method ?? "GET";
825
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
826
+ const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
827
+ const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
828
+ return fetch(proxyUrl, {
829
+ method: "POST",
830
+ headers: {
831
+ "Content-Type": "application/json",
832
+ Authorization: `Bearer ${token}`
833
+ },
834
+ body: JSON.stringify({
835
+ url: originalUrl,
836
+ method: originalMethod,
837
+ headers: normalizeHeaders(init?.headers),
838
+ body: originalBody
839
+ })
840
+ });
841
+ };
842
+ }
843
+ function createDeployedAppProxyFetch(connectionId) {
844
+ const projectId = process.env["SQUADBASE_PROJECT_ID"];
845
+ if (!projectId) {
846
+ throw new Error(
847
+ "Connection proxy is not configured. Please check your deployment settings."
848
+ );
849
+ }
850
+ const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
851
+ const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
852
+ return async (input, init) => {
853
+ const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
854
+ const originalMethod = init?.method ?? "GET";
855
+ const originalBody = init?.body ? JSON.parse(init.body) : void 0;
856
+ const c = getContext();
857
+ const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
858
+ if (!appSession) {
859
+ throw new Error(
860
+ "No authentication method available for connection proxy."
861
+ );
862
+ }
863
+ return fetch(proxyUrl, {
864
+ method: "POST",
865
+ headers: {
866
+ "Content-Type": "application/json",
867
+ Authorization: `Bearer ${appSession}`
868
+ },
869
+ body: JSON.stringify({
870
+ url: originalUrl,
871
+ method: originalMethod,
872
+ headers: normalizeHeaders(init?.headers),
873
+ body: originalBody
874
+ })
875
+ });
876
+ };
877
+ }
878
+ function createProxyFetch(connectionId) {
879
+ if (process.env.INTERNAL_SQUADBASE_SANDBOX_ID) {
880
+ return createSandboxProxyFetch(connectionId);
881
+ }
882
+ return createDeployedAppProxyFetch(connectionId);
883
+ }
884
+
885
+ // src/connectors/create-connector-sdk.ts
886
+ function loadConnectionsSync() {
887
+ const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
888
+ try {
889
+ const raw = readFileSync(filePath, "utf-8");
890
+ return JSON.parse(raw);
891
+ } catch {
892
+ return {};
893
+ }
894
+ }
895
+ function createConnectorSdk(plugin, createClient2) {
896
+ return (connectionId) => {
897
+ const connections = loadConnectionsSync();
898
+ const entry = connections[connectionId];
899
+ if (!entry) {
900
+ throw new Error(
901
+ `Connection "${connectionId}" not found in .squadbase/connections.json`
902
+ );
903
+ }
904
+ if (entry.connector.slug !== plugin.slug) {
905
+ throw new Error(
906
+ `Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
907
+ );
908
+ }
909
+ const params = {};
910
+ for (const param of Object.values(plugin.parameters)) {
911
+ if (param.required) {
912
+ params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
913
+ } else {
914
+ const val = resolveEnvVarOptional(entry, param.slug);
915
+ if (val !== void 0) params[param.slug] = val;
916
+ }
917
+ }
918
+ return createClient2(params, createProxyFetch(connectionId));
919
+ };
920
+ }
921
+
922
+ // src/connectors/entries/outlook-oauth.ts
923
+ var connection = createConnectorSdk(
924
+ outlookOauthConnector,
925
+ createClient
926
+ );
927
+ export {
928
+ connection
929
+ };