@squadbase/vite-server 0.1.3-dev.1 → 0.1.3-dev.10
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 +71031 -6800
- package/dist/connectors/airtable-oauth.js +77 -3
- package/dist/connectors/airtable.js +85 -2
- package/dist/connectors/amplitude.js +85 -2
- package/dist/connectors/anthropic.js +85 -2
- package/dist/connectors/asana.js +86 -3
- package/dist/connectors/attio.js +85 -2
- package/dist/connectors/customerio.js +86 -3
- package/dist/connectors/dbt.js +85 -2
- package/dist/connectors/gemini.js +86 -3
- package/dist/connectors/gmail-oauth.js +78 -4
- package/dist/connectors/gmail.d.ts +5 -0
- package/dist/connectors/gmail.js +875 -0
- package/dist/connectors/google-ads-oauth.js +78 -4
- package/dist/connectors/google-ads.js +85 -2
- package/dist/connectors/google-analytics-oauth.js +90 -8
- package/dist/connectors/google-analytics.js +85 -2
- package/dist/connectors/google-calendar-oauth.d.ts +5 -0
- package/dist/connectors/google-calendar-oauth.js +817 -0
- package/dist/connectors/google-calendar.d.ts +5 -0
- package/dist/connectors/google-calendar.js +991 -0
- package/dist/connectors/google-sheets-oauth.js +144 -33
- package/dist/connectors/google-sheets.js +119 -10
- package/dist/connectors/{microsoft-teams.d.ts → grafana.d.ts} +1 -1
- package/dist/connectors/grafana.js +638 -0
- package/dist/connectors/hubspot-oauth.js +77 -3
- package/dist/connectors/hubspot.js +79 -5
- package/dist/connectors/intercom-oauth.js +78 -4
- package/dist/connectors/intercom.js +86 -3
- package/dist/connectors/jira-api-key.js +84 -9
- package/dist/connectors/kintone-api-token.js +77 -3
- package/dist/connectors/kintone.js +86 -3
- package/dist/connectors/linkedin-ads-oauth.js +78 -4
- package/dist/connectors/linkedin-ads.js +86 -3
- package/dist/connectors/mailchimp-oauth.js +77 -3
- package/dist/connectors/mailchimp.js +86 -3
- package/dist/connectors/{microsoft-teams-oauth.d.ts → notion-oauth.d.ts} +1 -1
- package/dist/connectors/notion-oauth.js +567 -0
- package/dist/connectors/{slack.d.ts → notion.d.ts} +1 -1
- package/dist/connectors/notion.js +663 -0
- package/dist/connectors/openai.js +85 -2
- package/dist/connectors/shopify-oauth.js +77 -3
- package/dist/connectors/shopify.js +85 -2
- package/dist/connectors/stripe-api-key.d.ts +5 -0
- package/dist/connectors/stripe-api-key.js +600 -0
- package/dist/connectors/stripe-oauth.js +77 -3
- package/dist/connectors/wix-store.js +85 -2
- package/dist/connectors/zendesk-oauth.js +78 -4
- package/dist/connectors/zendesk.js +86 -3
- package/dist/index.js +75373 -8431
- package/dist/main.js +75359 -8417
- package/dist/vite-plugin.js +75210 -8305
- package/package.json +46 -2
- package/dist/connectors/microsoft-teams-oauth.js +0 -479
- package/dist/connectors/microsoft-teams.js +0 -381
- package/dist/connectors/slack.js +0 -362
|
@@ -0,0 +1,817 @@
|
|
|
1
|
+
// ../connectors/src/parameter-definition.ts
|
|
2
|
+
var ParameterDefinition = class {
|
|
3
|
+
slug;
|
|
4
|
+
name;
|
|
5
|
+
description;
|
|
6
|
+
envVarBaseKey;
|
|
7
|
+
type;
|
|
8
|
+
secret;
|
|
9
|
+
required;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.slug = config.slug;
|
|
12
|
+
this.name = config.name;
|
|
13
|
+
this.description = config.description;
|
|
14
|
+
this.envVarBaseKey = config.envVarBaseKey;
|
|
15
|
+
this.type = config.type;
|
|
16
|
+
this.secret = config.secret;
|
|
17
|
+
this.required = config.required;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get the parameter value from a ConnectorConnectionObject.
|
|
21
|
+
*/
|
|
22
|
+
getValue(connection2) {
|
|
23
|
+
const param = connection2.parameters.find(
|
|
24
|
+
(p) => p.parameterSlug === this.slug
|
|
25
|
+
);
|
|
26
|
+
if (!param || param.value == null) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Parameter "${this.slug}" not found or has no value in connection "${connection2.id}"`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return param.value;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Try to get the parameter value. Returns undefined if not found (for optional params).
|
|
35
|
+
*/
|
|
36
|
+
tryGetValue(connection2) {
|
|
37
|
+
const param = connection2.parameters.find(
|
|
38
|
+
(p) => p.parameterSlug === this.slug
|
|
39
|
+
);
|
|
40
|
+
if (!param || param.value == null) return void 0;
|
|
41
|
+
return param.value;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// ../connectors/src/connectors/google-calendar-oauth/parameters.ts
|
|
46
|
+
var parameters = {
|
|
47
|
+
calendarId: new ParameterDefinition({
|
|
48
|
+
slug: "calendar-id",
|
|
49
|
+
name: "Default Calendar ID",
|
|
50
|
+
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.",
|
|
51
|
+
envVarBaseKey: "GOOGLE_CALENDAR_OAUTH_CALENDAR_ID",
|
|
52
|
+
type: "text",
|
|
53
|
+
secret: false,
|
|
54
|
+
required: false
|
|
55
|
+
})
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// ../connectors/src/connectors/google-calendar-oauth/sdk/index.ts
|
|
59
|
+
var BASE_URL = "https://www.googleapis.com/calendar/v3";
|
|
60
|
+
function createClient(params, fetchFn = fetch) {
|
|
61
|
+
const defaultCalendarId = params[parameters.calendarId.slug] ?? "primary";
|
|
62
|
+
function resolveCalendarId(override) {
|
|
63
|
+
return override ?? defaultCalendarId;
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
async request(path2, init) {
|
|
67
|
+
const resolvedPath = path2.replace(
|
|
68
|
+
/\{calendarId\}/g,
|
|
69
|
+
defaultCalendarId
|
|
70
|
+
);
|
|
71
|
+
const url = `${BASE_URL}${resolvedPath.startsWith("/") ? "" : "/"}${resolvedPath}`;
|
|
72
|
+
return fetchFn(url, init);
|
|
73
|
+
},
|
|
74
|
+
async listCalendars() {
|
|
75
|
+
const response = await this.request("/users/me/calendarList", {
|
|
76
|
+
method: "GET"
|
|
77
|
+
});
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
const text = await response.text();
|
|
80
|
+
throw new Error(
|
|
81
|
+
`google-calendar-oauth: listCalendars failed (${response.status}): ${text}`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
const data = await response.json();
|
|
85
|
+
return data.items ?? [];
|
|
86
|
+
},
|
|
87
|
+
async listEvents(options, calendarId) {
|
|
88
|
+
const cid = resolveCalendarId(calendarId);
|
|
89
|
+
const searchParams = new URLSearchParams();
|
|
90
|
+
if (options?.timeMin) searchParams.set("timeMin", options.timeMin);
|
|
91
|
+
if (options?.timeMax) searchParams.set("timeMax", options.timeMax);
|
|
92
|
+
if (options?.maxResults)
|
|
93
|
+
searchParams.set("maxResults", String(options.maxResults));
|
|
94
|
+
if (options?.q) searchParams.set("q", options.q);
|
|
95
|
+
if (options?.singleEvents != null)
|
|
96
|
+
searchParams.set("singleEvents", String(options.singleEvents));
|
|
97
|
+
if (options?.orderBy) searchParams.set("orderBy", options.orderBy);
|
|
98
|
+
if (options?.pageToken) searchParams.set("pageToken", options.pageToken);
|
|
99
|
+
const qs = searchParams.toString();
|
|
100
|
+
const path2 = `/calendars/${encodeURIComponent(cid)}/events${qs ? `?${qs}` : ""}`;
|
|
101
|
+
const response = await this.request(path2, { method: "GET" });
|
|
102
|
+
if (!response.ok) {
|
|
103
|
+
const text = await response.text();
|
|
104
|
+
throw new Error(
|
|
105
|
+
`google-calendar-oauth: listEvents failed (${response.status}): ${text}`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
return await response.json();
|
|
109
|
+
},
|
|
110
|
+
async getEvent(eventId, calendarId) {
|
|
111
|
+
const cid = resolveCalendarId(calendarId);
|
|
112
|
+
const path2 = `/calendars/${encodeURIComponent(cid)}/events/${encodeURIComponent(eventId)}`;
|
|
113
|
+
const response = await this.request(path2, { method: "GET" });
|
|
114
|
+
if (!response.ok) {
|
|
115
|
+
const text = await response.text();
|
|
116
|
+
throw new Error(
|
|
117
|
+
`google-calendar-oauth: getEvent failed (${response.status}): ${text}`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
return await response.json();
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ../connectors/src/connector-onboarding.ts
|
|
126
|
+
var ConnectorOnboarding = class {
|
|
127
|
+
/** Phase 1: Connection setup instructions (optional — some connectors don't need this) */
|
|
128
|
+
connectionSetupInstructions;
|
|
129
|
+
/** Phase 2: Data overview instructions */
|
|
130
|
+
dataOverviewInstructions;
|
|
131
|
+
constructor(config) {
|
|
132
|
+
this.connectionSetupInstructions = config.connectionSetupInstructions;
|
|
133
|
+
this.dataOverviewInstructions = config.dataOverviewInstructions;
|
|
134
|
+
}
|
|
135
|
+
getConnectionSetupPrompt(language) {
|
|
136
|
+
return this.connectionSetupInstructions?.[language] ?? null;
|
|
137
|
+
}
|
|
138
|
+
getDataOverviewInstructions(language) {
|
|
139
|
+
return this.dataOverviewInstructions[language];
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// ../connectors/src/connector-tool.ts
|
|
144
|
+
var ConnectorTool = class {
|
|
145
|
+
name;
|
|
146
|
+
description;
|
|
147
|
+
inputSchema;
|
|
148
|
+
outputSchema;
|
|
149
|
+
_execute;
|
|
150
|
+
constructor(config) {
|
|
151
|
+
this.name = config.name;
|
|
152
|
+
this.description = config.description;
|
|
153
|
+
this.inputSchema = config.inputSchema;
|
|
154
|
+
this.outputSchema = config.outputSchema;
|
|
155
|
+
this._execute = config.execute;
|
|
156
|
+
}
|
|
157
|
+
createTool(connections, config) {
|
|
158
|
+
return {
|
|
159
|
+
description: this.description,
|
|
160
|
+
inputSchema: this.inputSchema,
|
|
161
|
+
outputSchema: this.outputSchema,
|
|
162
|
+
execute: (input) => this._execute(input, connections, config)
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// ../connectors/src/connector-plugin.ts
|
|
168
|
+
var ConnectorPlugin = class _ConnectorPlugin {
|
|
169
|
+
slug;
|
|
170
|
+
authType;
|
|
171
|
+
name;
|
|
172
|
+
description;
|
|
173
|
+
iconUrl;
|
|
174
|
+
parameters;
|
|
175
|
+
releaseFlag;
|
|
176
|
+
proxyPolicy;
|
|
177
|
+
experimentalAttributes;
|
|
178
|
+
onboarding;
|
|
179
|
+
systemPrompt;
|
|
180
|
+
tools;
|
|
181
|
+
query;
|
|
182
|
+
checkConnection;
|
|
183
|
+
constructor(config) {
|
|
184
|
+
this.slug = config.slug;
|
|
185
|
+
this.authType = config.authType;
|
|
186
|
+
this.name = config.name;
|
|
187
|
+
this.description = config.description;
|
|
188
|
+
this.iconUrl = config.iconUrl;
|
|
189
|
+
this.parameters = config.parameters;
|
|
190
|
+
this.releaseFlag = config.releaseFlag;
|
|
191
|
+
this.proxyPolicy = config.proxyPolicy;
|
|
192
|
+
this.experimentalAttributes = config.experimentalAttributes;
|
|
193
|
+
this.onboarding = config.onboarding;
|
|
194
|
+
this.systemPrompt = config.systemPrompt;
|
|
195
|
+
this.tools = config.tools;
|
|
196
|
+
this.query = config.query;
|
|
197
|
+
this.checkConnection = config.checkConnection;
|
|
198
|
+
}
|
|
199
|
+
get connectorKey() {
|
|
200
|
+
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Create tools for connections that belong to this connector.
|
|
204
|
+
* Filters connections by connectorKey internally.
|
|
205
|
+
* Returns tools keyed as `${connectorKey}_${toolName}`.
|
|
206
|
+
*/
|
|
207
|
+
createTools(connections, config) {
|
|
208
|
+
const myConnections = connections.filter(
|
|
209
|
+
(c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
|
|
210
|
+
);
|
|
211
|
+
const result = {};
|
|
212
|
+
for (const t of Object.values(this.tools)) {
|
|
213
|
+
result[`${this.connectorKey}_${t.name}`] = t.createTool(
|
|
214
|
+
myConnections,
|
|
215
|
+
config
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
return result;
|
|
219
|
+
}
|
|
220
|
+
static deriveKey(slug, authType) {
|
|
221
|
+
return authType ? `${slug}-${authType}` : slug;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// ../connectors/src/auth-types.ts
|
|
226
|
+
var AUTH_TYPES = {
|
|
227
|
+
OAUTH: "oauth",
|
|
228
|
+
API_KEY: "api-key",
|
|
229
|
+
JWT: "jwt",
|
|
230
|
+
SERVICE_ACCOUNT: "service-account",
|
|
231
|
+
PAT: "pat",
|
|
232
|
+
USER_PASSWORD: "user-password"
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// ../connectors/src/connectors/google-calendar-oauth/tools/list-calendars.ts
|
|
236
|
+
import { z } from "zod";
|
|
237
|
+
var BASE_URL2 = "https://www.googleapis.com/calendar/v3";
|
|
238
|
+
var REQUEST_TIMEOUT_MS = 6e4;
|
|
239
|
+
var cachedToken = null;
|
|
240
|
+
async function getProxyToken(config) {
|
|
241
|
+
if (cachedToken && cachedToken.expiresAt > Date.now() + 6e4) {
|
|
242
|
+
return cachedToken.token;
|
|
243
|
+
}
|
|
244
|
+
const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
|
|
245
|
+
const res = await fetch(url, {
|
|
246
|
+
method: "POST",
|
|
247
|
+
headers: {
|
|
248
|
+
"Content-Type": "application/json",
|
|
249
|
+
"x-api-key": config.appApiKey,
|
|
250
|
+
"project-id": config.projectId
|
|
251
|
+
},
|
|
252
|
+
body: JSON.stringify({
|
|
253
|
+
sandboxId: config.sandboxId,
|
|
254
|
+
issuedBy: "coding-agent"
|
|
255
|
+
})
|
|
256
|
+
});
|
|
257
|
+
if (!res.ok) {
|
|
258
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
259
|
+
throw new Error(
|
|
260
|
+
`Failed to get proxy token: HTTP ${res.status} ${errorText}`
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
const data = await res.json();
|
|
264
|
+
cachedToken = {
|
|
265
|
+
token: data.token,
|
|
266
|
+
expiresAt: new Date(data.expiresAt).getTime()
|
|
267
|
+
};
|
|
268
|
+
return data.token;
|
|
269
|
+
}
|
|
270
|
+
var inputSchema = z.object({
|
|
271
|
+
toolUseIntent: z.string().optional().describe(
|
|
272
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
273
|
+
),
|
|
274
|
+
connectionId: z.string().describe("ID of the Google Calendar OAuth connection to use")
|
|
275
|
+
});
|
|
276
|
+
var outputSchema = z.discriminatedUnion("success", [
|
|
277
|
+
z.object({
|
|
278
|
+
success: z.literal(true),
|
|
279
|
+
calendars: z.array(
|
|
280
|
+
z.object({
|
|
281
|
+
id: z.string(),
|
|
282
|
+
summary: z.string(),
|
|
283
|
+
primary: z.boolean().optional(),
|
|
284
|
+
accessRole: z.string()
|
|
285
|
+
})
|
|
286
|
+
)
|
|
287
|
+
}),
|
|
288
|
+
z.object({
|
|
289
|
+
success: z.literal(false),
|
|
290
|
+
error: z.string()
|
|
291
|
+
})
|
|
292
|
+
]);
|
|
293
|
+
var listCalendarsTool = new ConnectorTool({
|
|
294
|
+
name: "listCalendars",
|
|
295
|
+
description: "List all Google Calendars accessible with the OAuth credentials. Use during setup to discover available calendars.",
|
|
296
|
+
inputSchema,
|
|
297
|
+
outputSchema,
|
|
298
|
+
async execute({ connectionId }, connections, config) {
|
|
299
|
+
const connection2 = connections.find((c) => c.id === connectionId);
|
|
300
|
+
if (!connection2) {
|
|
301
|
+
return {
|
|
302
|
+
success: false,
|
|
303
|
+
error: `Connection ${connectionId} not found`
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
console.log(
|
|
307
|
+
`[connector-request] google-calendar-oauth/${connection2.name}: listCalendars`
|
|
308
|
+
);
|
|
309
|
+
try {
|
|
310
|
+
const url = `${BASE_URL2}/users/me/calendarList`;
|
|
311
|
+
const token = await getProxyToken(config.oauthProxy);
|
|
312
|
+
const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
313
|
+
const controller = new AbortController();
|
|
314
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
315
|
+
try {
|
|
316
|
+
const response = await fetch(proxyUrl, {
|
|
317
|
+
method: "POST",
|
|
318
|
+
headers: {
|
|
319
|
+
"Content-Type": "application/json",
|
|
320
|
+
Authorization: `Bearer ${token}`
|
|
321
|
+
},
|
|
322
|
+
body: JSON.stringify({
|
|
323
|
+
url,
|
|
324
|
+
method: "GET"
|
|
325
|
+
}),
|
|
326
|
+
signal: controller.signal
|
|
327
|
+
});
|
|
328
|
+
const data = await response.json();
|
|
329
|
+
if (!response.ok) {
|
|
330
|
+
const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
|
|
331
|
+
return { success: false, error: errorMessage };
|
|
332
|
+
}
|
|
333
|
+
const items = data.items ?? [];
|
|
334
|
+
return {
|
|
335
|
+
success: true,
|
|
336
|
+
calendars: items.map((c) => ({
|
|
337
|
+
id: c.id,
|
|
338
|
+
summary: c.summary,
|
|
339
|
+
primary: c.primary,
|
|
340
|
+
accessRole: c.accessRole
|
|
341
|
+
}))
|
|
342
|
+
};
|
|
343
|
+
} finally {
|
|
344
|
+
clearTimeout(timeout);
|
|
345
|
+
}
|
|
346
|
+
} catch (err) {
|
|
347
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
348
|
+
return { success: false, error: msg };
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// ../connectors/src/connectors/google-calendar-oauth/setup.ts
|
|
354
|
+
var listCalendarsToolName = `google-calendar-oauth_${listCalendarsTool.name}`;
|
|
355
|
+
var googleCalendarOauthOnboarding = new ConnectorOnboarding({
|
|
356
|
+
connectionSetupInstructions: {
|
|
357
|
+
ja: `\u4EE5\u4E0B\u306E\u624B\u9806\u3067Google Calendar (OAuth) \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
|
|
358
|
+
|
|
359
|
+
1. \`${listCalendarsToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001OAuth\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u30AB\u30EC\u30F3\u30C0\u30FC\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
|
|
360
|
+
2. \u300C\u4F7F\u7528\u3059\u308B\u30AB\u30EC\u30F3\u30C0\u30FC\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u300D\u3068\u30E6\u30FC\u30B6\u30FC\u306B\u4F1D\u3048\u305F\u4E0A\u3067\u3001\`askUserQuestion\` \u3092\u547C\u3073\u51FA\u3059:
|
|
361
|
+
- \`options\`: \u30AB\u30EC\u30F3\u30C0\u30FC\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u30AB\u30EC\u30F3\u30C0\u30FC\u540D (id: \u30AB\u30EC\u30F3\u30C0\u30FCID)\` \u306E\u5F62\u5F0F\u3001\`value\` \u306F\u30AB\u30EC\u30F3\u30C0\u30FCID
|
|
362
|
+
3. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
|
|
363
|
+
- \`parameterSlug\`: \`"calendar-id"\`
|
|
364
|
+
- \`value\`: \u9078\u629E\u3055\u308C\u305F\u30AB\u30EC\u30F3\u30C0\u30FCID
|
|
365
|
+
4. \`updateConnectionContext\` \u3092\u547C\u3073\u51FA\u3059:
|
|
366
|
+
- \`calendar\`: \u9078\u629E\u3055\u308C\u305F\u30AB\u30EC\u30F3\u30C0\u30FC\u540D
|
|
367
|
+
- \`calendarId\`: \u9078\u629E\u3055\u308C\u305F\u30AB\u30EC\u30F3\u30C0\u30FCID
|
|
368
|
+
- \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
|
|
369
|
+
|
|
370
|
+
#### \u5236\u7D04
|
|
371
|
+
- **\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u4E2D\u306B\u30A4\u30D9\u30F3\u30C8\u30C7\u30FC\u30BF\u3092\u53D6\u5F97\u3057\u306A\u3044\u3053\u3068**\u3002\u5B9F\u884C\u3057\u3066\u3088\u3044\u306E\u306F\u4E0A\u8A18\u624B\u9806\u3067\u6307\u5B9A\u3055\u308C\u305F\u30E1\u30BF\u30C7\u30FC\u30BF\u53D6\u5F97\u306E\u307F
|
|
372
|
+
- \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`,
|
|
373
|
+
en: `Follow these steps to set up the Google Calendar (OAuth) connection.
|
|
374
|
+
|
|
375
|
+
1. Call \`${listCalendarsToolName}\` to get the list of calendars accessible with the OAuth credentials
|
|
376
|
+
2. Tell the user "Please select a calendar.", then call \`askUserQuestion\`:
|
|
377
|
+
- \`options\`: The calendar list. Each option's \`label\` should be \`Calendar Name (id: calendarId)\`, \`value\` should be the calendar ID
|
|
378
|
+
3. Call \`updateConnectionParameters\`:
|
|
379
|
+
- \`parameterSlug\`: \`"calendar-id"\`
|
|
380
|
+
- \`value\`: The selected calendar ID
|
|
381
|
+
4. Call \`updateConnectionContext\`:
|
|
382
|
+
- \`calendar\`: The selected calendar's name
|
|
383
|
+
- \`calendarId\`: The selected calendar ID
|
|
384
|
+
- \`note\`: Brief description of the setup
|
|
385
|
+
|
|
386
|
+
#### Constraints
|
|
387
|
+
- **Do NOT fetch event data during setup**. Only the metadata requests specified in the steps above are allowed
|
|
388
|
+
- Write only 1 sentence between tool calls, then immediately call the next tool. Skip unnecessary explanations and proceed efficiently`
|
|
389
|
+
},
|
|
390
|
+
dataOverviewInstructions: {
|
|
391
|
+
en: `1. Call google-calendar-oauth_request with GET /calendars/{calendarId} to get the default calendar's metadata
|
|
392
|
+
2. Call google-calendar-oauth_request with GET /calendars/{calendarId}/events with query params timeMin (current RFC3339 timestamp), singleEvents=true, orderBy=startTime, and maxResults=10 to sample upcoming events`,
|
|
393
|
+
ja: `1. google-calendar-oauth_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
|
|
394
|
+
2. google-calendar-oauth_request \u3067 GET /calendars/{calendarId}/events \u3092\u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF timeMin\uFF08\u73FE\u5728\u306ERFC3339\u30BF\u30A4\u30E0\u30B9\u30BF\u30F3\u30D7\uFF09\u3001singleEvents=true\u3001orderBy=startTime\u3001maxResults=10 \u3067\u547C\u3073\u51FA\u3057\u3001\u76F4\u8FD1\u306E\u30A4\u30D9\u30F3\u30C8\u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0`
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// ../connectors/src/connectors/google-calendar-oauth/tools/request.ts
|
|
399
|
+
import { z as z2 } from "zod";
|
|
400
|
+
var BASE_URL3 = "https://www.googleapis.com/calendar/v3";
|
|
401
|
+
var REQUEST_TIMEOUT_MS2 = 6e4;
|
|
402
|
+
var cachedToken2 = null;
|
|
403
|
+
async function getProxyToken2(config) {
|
|
404
|
+
if (cachedToken2 && cachedToken2.expiresAt > Date.now() + 6e4) {
|
|
405
|
+
return cachedToken2.token;
|
|
406
|
+
}
|
|
407
|
+
const url = `${config.appApiBaseUrl}/v0/database/${config.projectId}/environment/${config.environmentId}/oauth-request-proxy-token`;
|
|
408
|
+
const res = await fetch(url, {
|
|
409
|
+
method: "POST",
|
|
410
|
+
headers: {
|
|
411
|
+
"Content-Type": "application/json",
|
|
412
|
+
"x-api-key": config.appApiKey,
|
|
413
|
+
"project-id": config.projectId
|
|
414
|
+
},
|
|
415
|
+
body: JSON.stringify({
|
|
416
|
+
sandboxId: config.sandboxId,
|
|
417
|
+
issuedBy: "coding-agent"
|
|
418
|
+
})
|
|
419
|
+
});
|
|
420
|
+
if (!res.ok) {
|
|
421
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
422
|
+
throw new Error(
|
|
423
|
+
`Failed to get proxy token: HTTP ${res.status} ${errorText}`
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
const data = await res.json();
|
|
427
|
+
cachedToken2 = {
|
|
428
|
+
token: data.token,
|
|
429
|
+
expiresAt: new Date(data.expiresAt).getTime()
|
|
430
|
+
};
|
|
431
|
+
return data.token;
|
|
432
|
+
}
|
|
433
|
+
var inputSchema2 = z2.object({
|
|
434
|
+
toolUseIntent: z2.string().optional().describe(
|
|
435
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
436
|
+
),
|
|
437
|
+
connectionId: z2.string().describe("ID of the Google Calendar OAuth connection to use"),
|
|
438
|
+
method: z2.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).describe("HTTP method"),
|
|
439
|
+
path: z2.string().describe(
|
|
440
|
+
"API path appended to https://www.googleapis.com/calendar/v3 (e.g., '/calendars/{calendarId}/events'). {calendarId} is automatically replaced."
|
|
441
|
+
),
|
|
442
|
+
queryParams: z2.record(z2.string(), z2.string()).optional().describe("Query parameters to append to the URL"),
|
|
443
|
+
body: z2.record(z2.string(), z2.unknown()).optional().describe("Request body (JSON) for POST/PUT/PATCH methods")
|
|
444
|
+
});
|
|
445
|
+
var outputSchema2 = z2.discriminatedUnion("success", [
|
|
446
|
+
z2.object({
|
|
447
|
+
success: z2.literal(true),
|
|
448
|
+
status: z2.number(),
|
|
449
|
+
data: z2.record(z2.string(), z2.unknown())
|
|
450
|
+
}),
|
|
451
|
+
z2.object({
|
|
452
|
+
success: z2.literal(false),
|
|
453
|
+
error: z2.string()
|
|
454
|
+
})
|
|
455
|
+
]);
|
|
456
|
+
var requestTool = new ConnectorTool({
|
|
457
|
+
name: "request",
|
|
458
|
+
description: `Send authenticated requests to the Google Calendar API v3.
|
|
459
|
+
Authentication is handled automatically via OAuth proxy.
|
|
460
|
+
{calendarId} in the path is automatically replaced with the connection's default calendar ID.`,
|
|
461
|
+
inputSchema: inputSchema2,
|
|
462
|
+
outputSchema: outputSchema2,
|
|
463
|
+
async execute({ connectionId, method, path: path2, queryParams, body }, connections, config) {
|
|
464
|
+
const connection2 = connections.find((c) => c.id === connectionId);
|
|
465
|
+
if (!connection2) {
|
|
466
|
+
return {
|
|
467
|
+
success: false,
|
|
468
|
+
error: `Connection ${connectionId} not found`
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
console.log(
|
|
472
|
+
`[connector-request] google-calendar-oauth/${connection2.name}: ${method} ${path2}`
|
|
473
|
+
);
|
|
474
|
+
try {
|
|
475
|
+
const calendarId = parameters.calendarId.tryGetValue(connection2) ?? "primary";
|
|
476
|
+
const resolvedPath = path2.replace(/\{calendarId\}/g, calendarId);
|
|
477
|
+
let url = `${BASE_URL3}${resolvedPath.startsWith("/") ? "" : "/"}${resolvedPath}`;
|
|
478
|
+
if (queryParams) {
|
|
479
|
+
const searchParams = new URLSearchParams(queryParams);
|
|
480
|
+
url += `?${searchParams.toString()}`;
|
|
481
|
+
}
|
|
482
|
+
const token = await getProxyToken2(config.oauthProxy);
|
|
483
|
+
const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
484
|
+
const controller = new AbortController();
|
|
485
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
|
|
486
|
+
try {
|
|
487
|
+
const response = await fetch(proxyUrl, {
|
|
488
|
+
method: "POST",
|
|
489
|
+
headers: {
|
|
490
|
+
"Content-Type": "application/json",
|
|
491
|
+
Authorization: `Bearer ${token}`
|
|
492
|
+
},
|
|
493
|
+
body: JSON.stringify({
|
|
494
|
+
url,
|
|
495
|
+
method,
|
|
496
|
+
...body && ["POST", "PUT", "PATCH"].includes(method) ? {
|
|
497
|
+
headers: { "Content-Type": "application/json" },
|
|
498
|
+
body: JSON.stringify(body)
|
|
499
|
+
} : {}
|
|
500
|
+
}),
|
|
501
|
+
signal: controller.signal
|
|
502
|
+
});
|
|
503
|
+
const data = await response.json();
|
|
504
|
+
if (!response.ok) {
|
|
505
|
+
const errorMessage = typeof data?.error === "string" ? data.error : typeof data?.message === "string" ? data.message : `HTTP ${response.status} ${response.statusText}`;
|
|
506
|
+
return { success: false, error: errorMessage };
|
|
507
|
+
}
|
|
508
|
+
return { success: true, status: response.status, data };
|
|
509
|
+
} finally {
|
|
510
|
+
clearTimeout(timeout);
|
|
511
|
+
}
|
|
512
|
+
} catch (err) {
|
|
513
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
514
|
+
return { success: false, error: msg };
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
// ../connectors/src/connectors/google-calendar-oauth/index.ts
|
|
520
|
+
var tools = {
|
|
521
|
+
request: requestTool,
|
|
522
|
+
listCalendars: listCalendarsTool
|
|
523
|
+
};
|
|
524
|
+
var googleCalendarOauthConnector = new ConnectorPlugin({
|
|
525
|
+
slug: "google-calendar",
|
|
526
|
+
authType: AUTH_TYPES.OAUTH,
|
|
527
|
+
name: "Google Calendar",
|
|
528
|
+
description: "Connect to Google Calendar for calendar and event data access using OAuth.",
|
|
529
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/2YsqoBEpdELmfDeFcyGHyE/4494c633b5ae15e562cb739cd85442c1/google-calendar.png",
|
|
530
|
+
parameters,
|
|
531
|
+
releaseFlag: { dev1: true, dev2: false, prod: false },
|
|
532
|
+
onboarding: googleCalendarOauthOnboarding,
|
|
533
|
+
proxyPolicy: {
|
|
534
|
+
allowlist: [
|
|
535
|
+
{
|
|
536
|
+
host: "www.googleapis.com",
|
|
537
|
+
methods: ["GET", "POST", "PUT", "PATCH", "DELETE"]
|
|
538
|
+
}
|
|
539
|
+
]
|
|
540
|
+
},
|
|
541
|
+
systemPrompt: {
|
|
542
|
+
en: `### Tools
|
|
543
|
+
|
|
544
|
+
- \`google-calendar-oauth_request\`: Send authenticated requests to the Google Calendar API v3. Use it to list calendars, get events, and manage calendar data. The {calendarId} placeholder in paths is automatically replaced. Authentication is configured automatically via OAuth.
|
|
545
|
+
- \`google-calendar-oauth_listCalendars\`: List accessible Google Calendars. Use during setup to discover available calendars.
|
|
546
|
+
|
|
547
|
+
### Google Calendar API v3 Reference
|
|
548
|
+
|
|
549
|
+
#### Available Endpoints
|
|
550
|
+
- GET \`/calendars/{calendarId}\` \u2014 Get calendar metadata
|
|
551
|
+
- GET \`/users/me/calendarList\` \u2014 List all calendars accessible by the authenticated user
|
|
552
|
+
- GET \`/calendars/{calendarId}/events\` \u2014 List events on a calendar
|
|
553
|
+
- GET \`/calendars/{calendarId}/events/{eventId}\` \u2014 Get a single event
|
|
554
|
+
|
|
555
|
+
#### Common Query Parameters for Event Listing
|
|
556
|
+
- \`timeMin\` \u2014 Lower bound (RFC3339 timestamp, e.g., "2024-01-01T00:00:00Z")
|
|
557
|
+
- \`timeMax\` \u2014 Upper bound (RFC3339 timestamp)
|
|
558
|
+
- \`maxResults\` \u2014 Maximum number of events (default 250, max 2500)
|
|
559
|
+
- \`singleEvents=true\` \u2014 Expand recurring events into individual instances
|
|
560
|
+
- \`orderBy=startTime\` \u2014 Order by start time (requires singleEvents=true)
|
|
561
|
+
- \`q\` \u2014 Free text search terms
|
|
562
|
+
|
|
563
|
+
#### Tips
|
|
564
|
+
- Use \`{calendarId}\` placeholder in paths \u2014 it is automatically replaced with the configured default calendar ID
|
|
565
|
+
- Set \`singleEvents=true\` to expand recurring events into individual instances
|
|
566
|
+
- When using \`orderBy=startTime\`, you must also set \`singleEvents=true\`
|
|
567
|
+
- Use RFC3339 format for time parameters (e.g., "2024-01-15T09:00:00Z" or "2024-01-15T09:00:00+09:00")
|
|
568
|
+
- The default calendar ID is "primary" if not configured
|
|
569
|
+
|
|
570
|
+
### Business Logic
|
|
571
|
+
|
|
572
|
+
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.
|
|
573
|
+
|
|
574
|
+
#### Example
|
|
575
|
+
|
|
576
|
+
\`\`\`ts
|
|
577
|
+
import { connection } from "@squadbase/vite-server/connectors/google-calendar-oauth";
|
|
578
|
+
|
|
579
|
+
const calendar = connection("<connectionId>");
|
|
580
|
+
|
|
581
|
+
// List all accessible calendars
|
|
582
|
+
const calendars = await calendar.listCalendars();
|
|
583
|
+
console.log(calendars.map(c => c.summary));
|
|
584
|
+
|
|
585
|
+
// List upcoming events
|
|
586
|
+
const now = new Date().toISOString();
|
|
587
|
+
const { items } = await calendar.listEvents({
|
|
588
|
+
timeMin: now,
|
|
589
|
+
maxResults: 10,
|
|
590
|
+
singleEvents: true,
|
|
591
|
+
orderBy: "startTime",
|
|
592
|
+
});
|
|
593
|
+
items.forEach(event => console.log(event.summary, event.start));
|
|
594
|
+
|
|
595
|
+
// Get a single event
|
|
596
|
+
const event = await calendar.getEvent("<eventId>");
|
|
597
|
+
console.log(event.summary, event.attendees);
|
|
598
|
+
\`\`\``,
|
|
599
|
+
ja: `### \u30C4\u30FC\u30EB
|
|
600
|
+
|
|
601
|
+
- \`google-calendar-oauth_request\`: Google Calendar API v3\u3078\u8A8D\u8A3C\u6E08\u307F\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u9001\u4FE1\u3057\u307E\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\u30D1\u30B9\u5185\u306E{calendarId}\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u306F\u81EA\u52D5\u7684\u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002OAuth\u7D4C\u7531\u3067\u8A8D\u8A3C\u306F\u81EA\u52D5\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002
|
|
602
|
+
- \`google-calendar-oauth_listCalendars\`: \u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGoogle Calendar\u306E\u4E00\u89A7\u3092\u53D6\u5F97\u3057\u307E\u3059\u3002\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u6642\u306B\u5229\u7528\u53EF\u80FD\u306A\u30AB\u30EC\u30F3\u30C0\u30FC\u3092\u78BA\u8A8D\u3059\u308B\u305F\u3081\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002
|
|
603
|
+
|
|
604
|
+
### Google Calendar API v3 \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
|
|
605
|
+
|
|
606
|
+
#### \u5229\u7528\u53EF\u80FD\u306A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8
|
|
607
|
+
- GET \`/calendars/{calendarId}\` \u2014 \u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u53D6\u5F97
|
|
608
|
+
- GET \`/users/me/calendarList\` \u2014 \u8A8D\u8A3C\u30E6\u30FC\u30B6\u30FC\u304C\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u5168\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u4E00\u89A7
|
|
609
|
+
- GET \`/calendars/{calendarId}/events\` \u2014 \u30AB\u30EC\u30F3\u30C0\u30FC\u4E0A\u306E\u30A4\u30D9\u30F3\u30C8\u4E00\u89A7
|
|
610
|
+
- GET \`/calendars/{calendarId}/events/{eventId}\` \u2014 \u5358\u4E00\u30A4\u30D9\u30F3\u30C8\u306E\u53D6\u5F97
|
|
611
|
+
|
|
612
|
+
#### \u30A4\u30D9\u30F3\u30C8\u4E00\u89A7\u306E\u4E3B\u8981\u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF
|
|
613
|
+
- \`timeMin\` \u2014 \u4E0B\u9650\uFF08RFC3339\u30BF\u30A4\u30E0\u30B9\u30BF\u30F3\u30D7\u3001\u4F8B: "2024-01-01T00:00:00Z"\uFF09
|
|
614
|
+
- \`timeMax\` \u2014 \u4E0A\u9650\uFF08RFC3339\u30BF\u30A4\u30E0\u30B9\u30BF\u30F3\u30D7\uFF09
|
|
615
|
+
- \`maxResults\` \u2014 \u6700\u5927\u30A4\u30D9\u30F3\u30C8\u6570\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8250\u3001\u6700\u59272500\uFF09
|
|
616
|
+
- \`singleEvents=true\` \u2014 \u7E70\u308A\u8FD4\u3057\u30A4\u30D9\u30F3\u30C8\u3092\u500B\u5225\u30A4\u30F3\u30B9\u30BF\u30F3\u30B9\u306B\u5C55\u958B
|
|
617
|
+
- \`orderBy=startTime\` \u2014 \u958B\u59CB\u6642\u9593\u9806\u306B\u4E26\u3079\u66FF\u3048\uFF08singleEvents=true\u304C\u5FC5\u8981\uFF09
|
|
618
|
+
- \`q\` \u2014 \u30D5\u30EA\u30FC\u30C6\u30AD\u30B9\u30C8\u691C\u7D22\u8A9E
|
|
619
|
+
|
|
620
|
+
#### \u30D2\u30F3\u30C8
|
|
621
|
+
- \u30D1\u30B9\u306B \`{calendarId}\` \u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u3092\u4F7F\u7528 \u2014 \u8A2D\u5B9A\u6E08\u307F\u306E\u30C7\u30D5\u30A9\u30EB\u30C8\u30AB\u30EC\u30F3\u30C0\u30FCID\u3067\u81EA\u52D5\u7684\u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059
|
|
622
|
+
- \`singleEvents=true\` \u3092\u8A2D\u5B9A\u3059\u308B\u3068\u3001\u7E70\u308A\u8FD4\u3057\u30A4\u30D9\u30F3\u30C8\u304C\u500B\u5225\u306E\u30A4\u30F3\u30B9\u30BF\u30F3\u30B9\u306B\u5C55\u958B\u3055\u308C\u307E\u3059
|
|
623
|
+
- \`orderBy=startTime\` \u3092\u4F7F\u7528\u3059\u308B\u5834\u5408\u3001\`singleEvents=true\` \u3082\u8A2D\u5B9A\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059
|
|
624
|
+
- \u6642\u9593\u30D1\u30E9\u30E1\u30FC\u30BF\u306B\u306FRFC3339\u5F62\u5F0F\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4F8B: "2024-01-15T09:00:00Z" \u3084 "2024-01-15T09:00:00+09:00"\uFF09
|
|
625
|
+
- \u30C7\u30D5\u30A9\u30EB\u30C8\u30AB\u30EC\u30F3\u30C0\u30FCID\u306F\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u306A\u3044\u5834\u5408 "primary" \u304C\u4F7F\u7528\u3055\u308C\u307E\u3059
|
|
626
|
+
|
|
627
|
+
### Business Logic
|
|
628
|
+
|
|
629
|
+
\u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u4EE5\u4E0B\u306B\u793A\u3059\u30B3\u30CD\u30AF\u30BFSDK\u3092\u4F7F\u7528\u3057\u3066\u30CF\u30F3\u30C9\u30E9\u30B3\u30FC\u30C9\u3092\u8A18\u8FF0\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u76F4\u63A5\u8A8D\u8A3C\u60C5\u5831\u306B\u30A2\u30AF\u30BB\u30B9\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
|
|
630
|
+
|
|
631
|
+
#### Example
|
|
632
|
+
|
|
633
|
+
\`\`\`ts
|
|
634
|
+
import { connection } from "@squadbase/vite-server/connectors/google-calendar-oauth";
|
|
635
|
+
|
|
636
|
+
const calendar = connection("<connectionId>");
|
|
637
|
+
|
|
638
|
+
// List all accessible calendars
|
|
639
|
+
const calendars = await calendar.listCalendars();
|
|
640
|
+
console.log(calendars.map(c => c.summary));
|
|
641
|
+
|
|
642
|
+
// List upcoming events
|
|
643
|
+
const now = new Date().toISOString();
|
|
644
|
+
const { items } = await calendar.listEvents({
|
|
645
|
+
timeMin: now,
|
|
646
|
+
maxResults: 10,
|
|
647
|
+
singleEvents: true,
|
|
648
|
+
orderBy: "startTime",
|
|
649
|
+
});
|
|
650
|
+
items.forEach(event => console.log(event.summary, event.start));
|
|
651
|
+
|
|
652
|
+
// Get a single event
|
|
653
|
+
const event = await calendar.getEvent("<eventId>");
|
|
654
|
+
console.log(event.summary, event.attendees);
|
|
655
|
+
\`\`\``
|
|
656
|
+
},
|
|
657
|
+
tools,
|
|
658
|
+
async checkConnection(params, config) {
|
|
659
|
+
const { proxyFetch } = config;
|
|
660
|
+
const calendarId = params[parameters.calendarId.slug] ?? "primary";
|
|
661
|
+
const url = `https://www.googleapis.com/calendar/v3/calendars/${encodeURIComponent(calendarId)}`;
|
|
662
|
+
try {
|
|
663
|
+
const res = await proxyFetch(url, { method: "GET" });
|
|
664
|
+
if (!res.ok) {
|
|
665
|
+
const errorText = await res.text().catch(() => res.statusText);
|
|
666
|
+
return {
|
|
667
|
+
success: false,
|
|
668
|
+
error: `Google Calendar API failed: HTTP ${res.status} ${errorText}`
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
return { success: true };
|
|
672
|
+
} catch (error) {
|
|
673
|
+
return {
|
|
674
|
+
success: false,
|
|
675
|
+
error: error instanceof Error ? error.message : String(error)
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
// src/connectors/create-connector-sdk.ts
|
|
682
|
+
import { readFileSync } from "fs";
|
|
683
|
+
import path from "path";
|
|
684
|
+
|
|
685
|
+
// src/connector-client/env.ts
|
|
686
|
+
function resolveEnvVar(entry, key, connectionId) {
|
|
687
|
+
const envVarName = entry.envVars[key];
|
|
688
|
+
if (!envVarName) {
|
|
689
|
+
throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
|
|
690
|
+
}
|
|
691
|
+
const value = process.env[envVarName];
|
|
692
|
+
if (!value) {
|
|
693
|
+
throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
|
|
694
|
+
}
|
|
695
|
+
return value;
|
|
696
|
+
}
|
|
697
|
+
function resolveEnvVarOptional(entry, key) {
|
|
698
|
+
const envVarName = entry.envVars[key];
|
|
699
|
+
if (!envVarName) return void 0;
|
|
700
|
+
return process.env[envVarName] || void 0;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// src/connector-client/proxy-fetch.ts
|
|
704
|
+
import { getContext } from "hono/context-storage";
|
|
705
|
+
import { getCookie } from "hono/cookie";
|
|
706
|
+
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
707
|
+
function createSandboxProxyFetch(connectionId) {
|
|
708
|
+
return async (input, init) => {
|
|
709
|
+
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
710
|
+
const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
|
|
711
|
+
if (!token || !sandboxId) {
|
|
712
|
+
throw new Error(
|
|
713
|
+
"Connection proxy is not configured. Please check your deployment settings."
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
717
|
+
const originalMethod = init?.method ?? "GET";
|
|
718
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
719
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
720
|
+
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
721
|
+
return fetch(proxyUrl, {
|
|
722
|
+
method: "POST",
|
|
723
|
+
headers: {
|
|
724
|
+
"Content-Type": "application/json",
|
|
725
|
+
Authorization: `Bearer ${token}`
|
|
726
|
+
},
|
|
727
|
+
body: JSON.stringify({
|
|
728
|
+
url: originalUrl,
|
|
729
|
+
method: originalMethod,
|
|
730
|
+
body: originalBody
|
|
731
|
+
})
|
|
732
|
+
});
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
function createDeployedAppProxyFetch(connectionId) {
|
|
736
|
+
const projectId = process.env["SQUADBASE_PROJECT_ID"];
|
|
737
|
+
if (!projectId) {
|
|
738
|
+
throw new Error(
|
|
739
|
+
"Connection proxy is not configured. Please check your deployment settings."
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
743
|
+
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
744
|
+
return async (input, init) => {
|
|
745
|
+
const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
746
|
+
const originalMethod = init?.method ?? "GET";
|
|
747
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
748
|
+
const c = getContext();
|
|
749
|
+
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
750
|
+
if (!appSession) {
|
|
751
|
+
throw new Error(
|
|
752
|
+
"No authentication method available for connection proxy."
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
return fetch(proxyUrl, {
|
|
756
|
+
method: "POST",
|
|
757
|
+
headers: {
|
|
758
|
+
"Content-Type": "application/json",
|
|
759
|
+
Authorization: `Bearer ${appSession}`
|
|
760
|
+
},
|
|
761
|
+
body: JSON.stringify({
|
|
762
|
+
url: originalUrl,
|
|
763
|
+
method: originalMethod,
|
|
764
|
+
body: originalBody
|
|
765
|
+
})
|
|
766
|
+
});
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
function createProxyFetch(connectionId) {
|
|
770
|
+
if (process.env.INTERNAL_SQUADBASE_SANDBOX_ID) {
|
|
771
|
+
return createSandboxProxyFetch(connectionId);
|
|
772
|
+
}
|
|
773
|
+
return createDeployedAppProxyFetch(connectionId);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// src/connectors/create-connector-sdk.ts
|
|
777
|
+
function loadConnectionsSync() {
|
|
778
|
+
const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
|
|
779
|
+
try {
|
|
780
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
781
|
+
return JSON.parse(raw);
|
|
782
|
+
} catch {
|
|
783
|
+
return {};
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
function createConnectorSdk(plugin, createClient2) {
|
|
787
|
+
return (connectionId) => {
|
|
788
|
+
const connections = loadConnectionsSync();
|
|
789
|
+
const entry = connections[connectionId];
|
|
790
|
+
if (!entry) {
|
|
791
|
+
throw new Error(
|
|
792
|
+
`Connection "${connectionId}" not found in .squadbase/connections.json`
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
if (entry.connector.slug !== plugin.slug) {
|
|
796
|
+
throw new Error(
|
|
797
|
+
`Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
const params = {};
|
|
801
|
+
for (const param of Object.values(plugin.parameters)) {
|
|
802
|
+
if (param.required) {
|
|
803
|
+
params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
|
|
804
|
+
} else {
|
|
805
|
+
const val = resolveEnvVarOptional(entry, param.slug);
|
|
806
|
+
if (val !== void 0) params[param.slug] = val;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
return createClient2(params, createProxyFetch(connectionId));
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// src/connectors/entries/google-calendar-oauth.ts
|
|
814
|
+
var connection = createConnectorSdk(googleCalendarOauthConnector, createClient);
|
|
815
|
+
export {
|
|
816
|
+
connection
|
|
817
|
+
};
|