@squadbase/vite-server 0.1.3-dev.5 → 0.1.3-dev.6

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.
@@ -60,7 +60,7 @@ var parameters = {
60
60
  envVarBaseKey: "GOOGLE_ADS_DEVELOPER_TOKEN",
61
61
  type: "text",
62
62
  secret: true,
63
- required: false
63
+ required: true
64
64
  })
65
65
  };
66
66
 
@@ -59,11 +59,11 @@ var parameters = {
59
59
  propertyId: new ParameterDefinition({
60
60
  slug: "property-id",
61
61
  name: "Google Analytics Property ID",
62
- description: "The Google Analytics 4 property ID (e.g., 123456789).",
62
+ description: "The Google Analytics 4 property ID (e.g., 123456789). Set during connection setup.",
63
63
  envVarBaseKey: "GA_PROPERTY_ID",
64
64
  type: "text",
65
65
  secret: false,
66
- required: true
66
+ required: false
67
67
  })
68
68
  };
69
69
 
@@ -125,7 +125,7 @@ function createClient(params) {
125
125
  }
126
126
  let cachedToken = null;
127
127
  let tokenExpiresAt = 0;
128
- async function getAccessToken() {
128
+ async function getAccessToken2() {
129
129
  const nowSec = Math.floor(Date.now() / 1e3);
130
130
  if (cachedToken && nowSec < tokenExpiresAt - 60) {
131
131
  return cachedToken;
@@ -156,7 +156,7 @@ function createClient(params) {
156
156
  }
157
157
  return {
158
158
  async request(path2, init) {
159
- const accessToken = await getAccessToken();
159
+ const accessToken = await getAccessToken2();
160
160
  const resolvedPath = path2.replace(/\{propertyId\}/g, propertyId);
161
161
  const url = `${BASE_URL.replace(/\/+$/, "")}/${resolvedPath.replace(/^\/+/, "")}`;
162
162
  const headers = new Headers(init?.headers);
@@ -331,8 +331,230 @@ var AUTH_TYPES = {
331
331
  USER_PASSWORD: "user-password"
332
332
  };
333
333
 
334
+ // ../connectors/src/connectors/google-analytics/tools/list-accounts.ts
335
+ import { z } from "zod";
336
+
337
+ // ../connectors/src/connectors/google-analytics/tools/auth.ts
338
+ var SCOPES = [
339
+ "https://www.googleapis.com/auth/analytics.readonly"
340
+ ];
341
+ async function getAccessToken(connection2) {
342
+ const { GoogleAuth } = await import("google-auth-library");
343
+ const keyJsonBase64 = parameters.serviceAccountKeyJsonBase64.getValue(connection2);
344
+ const credentials = JSON.parse(
345
+ Buffer.from(keyJsonBase64, "base64").toString("utf-8")
346
+ );
347
+ const auth = new GoogleAuth({
348
+ credentials,
349
+ scopes: SCOPES
350
+ });
351
+ const token = await auth.getAccessToken();
352
+ if (!token) {
353
+ throw new Error("Failed to obtain access token");
354
+ }
355
+ return token;
356
+ }
357
+
358
+ // ../connectors/src/connectors/google-analytics/tools/list-accounts.ts
359
+ var ADMIN_BASE_URL = "https://analyticsadmin.googleapis.com/v1beta/";
360
+ var REQUEST_TIMEOUT_MS = 6e4;
361
+ var inputSchema = z.object({
362
+ toolUseIntent: z.string().optional().describe(
363
+ "Brief description of what you intend to accomplish with this tool call"
364
+ ),
365
+ connectionId: z.string().describe("ID of the Google Analytics connection to use")
366
+ });
367
+ var outputSchema = z.discriminatedUnion("success", [
368
+ z.object({
369
+ success: z.literal(true),
370
+ accounts: z.array(
371
+ z.object({
372
+ name: z.string(),
373
+ displayName: z.string()
374
+ })
375
+ )
376
+ }),
377
+ z.object({
378
+ success: z.literal(false),
379
+ error: z.string()
380
+ })
381
+ ]);
382
+ var listAccountsTool = new ConnectorTool({
383
+ name: "listAccounts",
384
+ description: "List Google Analytics accounts accessible with the service account credentials. Returns account names and display names.",
385
+ inputSchema,
386
+ outputSchema,
387
+ async execute({ connectionId }, connections) {
388
+ const connection2 = connections.find((c) => c.id === connectionId);
389
+ if (!connection2) {
390
+ return {
391
+ success: false,
392
+ error: `Connection ${connectionId} not found`
393
+ };
394
+ }
395
+ console.log(
396
+ `[connector-request] google-analytics/${connection2.name}: listAccounts`
397
+ );
398
+ try {
399
+ const token = await getAccessToken(connection2);
400
+ const controller = new AbortController();
401
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
402
+ try {
403
+ const response = await fetch(`${ADMIN_BASE_URL}accounts`, {
404
+ method: "GET",
405
+ headers: {
406
+ Authorization: `Bearer ${token}`
407
+ },
408
+ signal: controller.signal
409
+ });
410
+ const data = await response.json();
411
+ if (!response.ok) {
412
+ const errorMessage = data?.error?.message ?? `HTTP ${response.status} ${response.statusText}`;
413
+ return { success: false, error: errorMessage };
414
+ }
415
+ const accounts = (data.accounts ?? []).map((a) => ({
416
+ name: a.name ?? "",
417
+ displayName: a.displayName ?? ""
418
+ }));
419
+ return { success: true, accounts };
420
+ } finally {
421
+ clearTimeout(timeout);
422
+ }
423
+ } catch (err) {
424
+ const msg = err instanceof Error ? err.message : String(err);
425
+ return { success: false, error: msg };
426
+ }
427
+ }
428
+ });
429
+
430
+ // ../connectors/src/connectors/google-analytics/tools/list-properties.ts
431
+ import { z as z2 } from "zod";
432
+ var ADMIN_BASE_URL2 = "https://analyticsadmin.googleapis.com/v1beta/";
433
+ var REQUEST_TIMEOUT_MS2 = 6e4;
434
+ var inputSchema2 = z2.object({
435
+ toolUseIntent: z2.string().optional().describe(
436
+ "Brief description of what you intend to accomplish with this tool call"
437
+ ),
438
+ connectionId: z2.string().describe("ID of the Google Analytics connection to use"),
439
+ accountName: z2.string().describe(
440
+ "The account resource name (e.g., 'accounts/123456'). Obtained from the listAccounts tool."
441
+ )
442
+ });
443
+ var outputSchema2 = z2.discriminatedUnion("success", [
444
+ z2.object({
445
+ success: z2.literal(true),
446
+ properties: z2.array(
447
+ z2.object({
448
+ name: z2.string(),
449
+ displayName: z2.string(),
450
+ propertyId: z2.string()
451
+ })
452
+ )
453
+ }),
454
+ z2.object({
455
+ success: z2.literal(false),
456
+ error: z2.string()
457
+ })
458
+ ]);
459
+ var listPropertiesTool = new ConnectorTool({
460
+ name: "listProperties",
461
+ description: "List GA4 properties for a given Google Analytics account. Returns property names, display names, and property IDs.",
462
+ inputSchema: inputSchema2,
463
+ outputSchema: outputSchema2,
464
+ async execute({ connectionId, accountName }, connections) {
465
+ const connection2 = connections.find((c) => c.id === connectionId);
466
+ if (!connection2) {
467
+ return {
468
+ success: false,
469
+ error: `Connection ${connectionId} not found`
470
+ };
471
+ }
472
+ console.log(
473
+ `[connector-request] google-analytics/${connection2.name}: listProperties for ${accountName}`
474
+ );
475
+ try {
476
+ const token = await getAccessToken(connection2);
477
+ const controller = new AbortController();
478
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
479
+ try {
480
+ const filter = encodeURIComponent(`parent:${accountName}`);
481
+ const response = await fetch(
482
+ `${ADMIN_BASE_URL2}properties?filter=${filter}`,
483
+ {
484
+ method: "GET",
485
+ headers: {
486
+ Authorization: `Bearer ${token}`
487
+ },
488
+ signal: controller.signal
489
+ }
490
+ );
491
+ const data = await response.json();
492
+ if (!response.ok) {
493
+ const errorMessage = data?.error?.message ?? `HTTP ${response.status} ${response.statusText}`;
494
+ return { success: false, error: errorMessage };
495
+ }
496
+ const properties = (data.properties ?? []).map((p) => {
497
+ const name = p.name ?? "";
498
+ const propertyId = name.replace(/^properties\//, "");
499
+ return {
500
+ name,
501
+ displayName: p.displayName ?? "",
502
+ propertyId
503
+ };
504
+ });
505
+ return { success: true, properties };
506
+ } finally {
507
+ clearTimeout(timeout);
508
+ }
509
+ } catch (err) {
510
+ const msg = err instanceof Error ? err.message : String(err);
511
+ return { success: false, error: msg };
512
+ }
513
+ }
514
+ });
515
+
334
516
  // ../connectors/src/connectors/google-analytics/setup.ts
517
+ var listAccountsToolName = `google-analytics_${listAccountsTool.name}`;
518
+ var listPropertiesToolName = `google-analytics_${listPropertiesTool.name}`;
335
519
  var googleAnalyticsOnboarding = new ConnectorOnboarding({
520
+ connectionSetupInstructions: {
521
+ ja: `\u4EE5\u4E0B\u306E\u624B\u9806\u3067Google Analytics (Service Account) \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
522
+
523
+ 1. \`${listAccountsToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001Service Account\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGoogle Analytics\u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
524
+ 2. \u300C\u4F7F\u7528\u3059\u308B\u30A2\u30AB\u30A6\u30F3\u30C8\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:
525
+ - \`options\`: \u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u8868\u793A\u540D (name)\` \u306E\u5F62\u5F0F
526
+ 3. \`${listPropertiesToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001\u9078\u629E\u3055\u308C\u305F\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u30D7\u30ED\u30D1\u30C6\u30A3\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
527
+ 4. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
528
+ - \`parameterSlug\`: \`"property-id"\`
529
+ - \`options\`: \u30D7\u30ED\u30D1\u30C6\u30A3\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u8868\u793A\u540D (id: \u30D7\u30ED\u30D1\u30C6\u30A3ID)\` \u306E\u5F62\u5F0F\u3001\`value\` \u306F\u30D7\u30ED\u30D1\u30C6\u30A3ID
530
+ 5. \u30E6\u30FC\u30B6\u30FC\u304C\u9078\u629E\u3057\u305F\u30D7\u30ED\u30D1\u30C6\u30A3\u306E \`label\` \u304C\u30E1\u30C3\u30BB\u30FC\u30B8\u3068\u3057\u3066\u5C4A\u304F\u306E\u3067\u3001\u6B21\u306E\u30B9\u30C6\u30C3\u30D7\u306B\u9032\u3080
531
+ 6. \`updateConnectionContext\` \u3092\u547C\u3073\u51FA\u3059:
532
+ - \`property\`: \u9078\u629E\u3055\u308C\u305F\u30D7\u30ED\u30D1\u30C6\u30A3\u306E\u8868\u793A\u540D
533
+ - \`propertyId\`: \u9078\u629E\u3055\u308C\u305F\u30D7\u30ED\u30D1\u30C6\u30A3ID
534
+ - \`note\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5185\u5BB9\u306E\u7C21\u5358\u306A\u8AAC\u660E
535
+
536
+ #### \u5236\u7D04
537
+ - **\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u4E2D\u306B\u30EC\u30DD\u30FC\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
538
+ - \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`,
539
+ en: `Follow these steps to set up the Google Analytics (Service Account) connection.
540
+
541
+ 1. Call \`${listAccountsToolName}\` to get the list of Google Analytics accounts accessible with the service account credentials
542
+ 2. Tell the user "Please select an account.", then call \`askUserQuestion\`:
543
+ - \`options\`: The account list. Each option's \`label\` should be \`Display Name (name)\`
544
+ 3. Call \`${listPropertiesToolName}\` to get the list of properties for the selected account
545
+ 4. Call \`updateConnectionParameters\`:
546
+ - \`parameterSlug\`: \`"property-id"\`
547
+ - \`options\`: The property list. Each option's \`label\` should be \`Display Name (id: propertyId)\`, \`value\` should be the property ID
548
+ 5. The \`label\` of the user's selected property will arrive as a message. Proceed to the next step
549
+ 6. Call \`updateConnectionContext\`:
550
+ - \`property\`: The selected property's display name
551
+ - \`propertyId\`: The selected property ID
552
+ - \`note\`: Brief description of the setup
553
+
554
+ #### Constraints
555
+ - **Do NOT fetch report data during setup**. Only the metadata requests specified in the steps above are allowed
556
+ - Write only 1 sentence between tool calls, then immediately call the next tool. Skip unnecessary explanations and proceed efficiently`
557
+ },
336
558
  dataOverviewInstructions: {
337
559
  en: `1. Call google-analytics_request with GET properties/{propertyId}/metadata to list available dimensions and metrics
338
560
  2. Call google-analytics_request with POST properties/{propertyId}:runReport using a small date range and basic metrics to verify data availability`,
@@ -342,25 +564,25 @@ var googleAnalyticsOnboarding = new ConnectorOnboarding({
342
564
  });
343
565
 
344
566
  // ../connectors/src/connectors/google-analytics/tools/request.ts
345
- import { z } from "zod";
567
+ import { z as z3 } from "zod";
346
568
  var BASE_URL2 = "https://analyticsdata.googleapis.com/v1beta/";
347
- var REQUEST_TIMEOUT_MS = 6e4;
348
- var inputSchema = z.object({
349
- toolUseIntent: z.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
350
- connectionId: z.string().describe("ID of the Google Analytics connection to use"),
351
- method: z.enum(["GET", "POST"]).describe("HTTP method"),
352
- path: z.string().describe("API path (e.g., 'properties/{propertyId}:runReport'). {propertyId} is automatically replaced."),
353
- body: z.record(z.string(), z.unknown()).optional().describe("POST request body (JSON)")
569
+ var REQUEST_TIMEOUT_MS3 = 6e4;
570
+ var inputSchema3 = z3.object({
571
+ toolUseIntent: z3.string().optional().describe("Brief description of what you intend to accomplish with this tool call"),
572
+ connectionId: z3.string().describe("ID of the Google Analytics connection to use"),
573
+ method: z3.enum(["GET", "POST"]).describe("HTTP method"),
574
+ path: z3.string().describe("API path (e.g., 'properties/{propertyId}:runReport'). {propertyId} is automatically replaced."),
575
+ body: z3.record(z3.string(), z3.unknown()).optional().describe("POST request body (JSON)")
354
576
  });
355
- var outputSchema = z.discriminatedUnion("success", [
356
- z.object({
357
- success: z.literal(true),
358
- status: z.number(),
359
- data: z.record(z.string(), z.unknown())
577
+ var outputSchema3 = z3.discriminatedUnion("success", [
578
+ z3.object({
579
+ success: z3.literal(true),
580
+ status: z3.number(),
581
+ data: z3.record(z3.string(), z3.unknown())
360
582
  }),
361
- z.object({
362
- success: z.literal(false),
363
- error: z.string()
583
+ z3.object({
584
+ success: z3.literal(false),
585
+ error: z3.string()
364
586
  })
365
587
  ]);
366
588
  var requestTool = new ConnectorTool({
@@ -368,8 +590,8 @@ var requestTool = new ConnectorTool({
368
590
  description: `Send authenticated requests to the Google Analytics Data API.
369
591
  Authentication is handled automatically using a service account.
370
592
  {propertyId} in the path is automatically replaced with the connection's property-id.`,
371
- inputSchema,
372
- outputSchema,
593
+ inputSchema: inputSchema3,
594
+ outputSchema: outputSchema3,
373
595
  async execute({ connectionId, method, path: path2, body }, connections) {
374
596
  const connection2 = connections.find((c) => c.id === connectionId);
375
597
  if (!connection2) {
@@ -379,7 +601,7 @@ Authentication is handled automatically using a service account.
379
601
  try {
380
602
  const { GoogleAuth } = await import("google-auth-library");
381
603
  const keyJsonBase64 = parameters.serviceAccountKeyJsonBase64.getValue(connection2);
382
- const propertyId = parameters.propertyId.getValue(connection2);
604
+ const propertyId = parameters.propertyId.tryGetValue(connection2);
383
605
  const credentials = JSON.parse(
384
606
  Buffer.from(keyJsonBase64, "base64").toString("utf-8")
385
607
  );
@@ -391,10 +613,10 @@ Authentication is handled automatically using a service account.
391
613
  if (!token) {
392
614
  return { success: false, error: "Failed to obtain access token" };
393
615
  }
394
- const resolvedPath = path2.replace(/\{propertyId\}/g, propertyId);
616
+ const resolvedPath = propertyId ? path2.replace(/\{propertyId\}/g, propertyId) : path2;
395
617
  const url = `${BASE_URL2}${resolvedPath}`;
396
618
  const controller = new AbortController();
397
- const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
619
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS3);
398
620
  try {
399
621
  const response = await fetch(url, {
400
622
  method,
@@ -425,7 +647,11 @@ Authentication is handled automatically using a service account.
425
647
  });
426
648
 
427
649
  // ../connectors/src/connectors/google-analytics/index.ts
428
- var tools = { request: requestTool };
650
+ var tools = {
651
+ request: requestTool,
652
+ listAccounts: listAccountsTool,
653
+ listProperties: listPropertiesTool
654
+ };
429
655
  var googleAnalyticsConnector = new ConnectorPlugin({
430
656
  slug: "google-analytics",
431
657
  authType: AUTH_TYPES.SERVICE_ACCOUNT,
@@ -439,6 +665,8 @@ var googleAnalyticsConnector = new ConnectorPlugin({
439
665
  en: `### Tools
440
666
 
441
667
  - \`google-analytics_request\`: The only way to call the GA4 Data API. Use it to fetch metadata, run reports, or run realtime reports. See the GA4 Data API Reference below for available endpoints and request bodies.
668
+ - \`google-analytics_listAccounts\`: List accessible Google Analytics accounts. Use during setup to discover available accounts.
669
+ - \`google-analytics_listProperties\`: List GA4 properties for a given account. Use during setup to select the target property.
442
670
 
443
671
  ### Business Logic
444
672
 
@@ -507,6 +735,8 @@ activeUsers, sessions, screenPageViews, bounceRate, averageSessionDuration, conv
507
735
  ja: `### \u30C4\u30FC\u30EB
508
736
 
509
737
  - \`google-analytics_request\`: GA4 Data API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30E1\u30BF\u30C7\u30FC\u30BF\u306E\u53D6\u5F97\u3001\u30EC\u30DD\u30FC\u30C8\u306E\u5B9F\u884C\u3001\u30EA\u30A2\u30EB\u30BF\u30A4\u30E0\u30EC\u30DD\u30FC\u30C8\u306E\u5B9F\u884C\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u5229\u7528\u53EF\u80FD\u306A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3068\u30EA\u30AF\u30A8\u30B9\u30C8\u30DC\u30C7\u30A3\u306F\u4E0B\u90E8\u306E\u300CGA4 Data API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\u300D\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002
738
+ - \`google-analytics_listAccounts\`: \u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGoogle Analytics\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u4E00\u89A7\u3092\u53D6\u5F97\u3057\u307E\u3059\u3002\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u6642\u306B\u5229\u7528\u53EF\u80FD\u306A\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u78BA\u8A8D\u3059\u308B\u305F\u3081\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002
739
+ - \`google-analytics_listProperties\`: \u6307\u5B9A\u30A2\u30AB\u30A6\u30F3\u30C8\u306EGA4\u30D7\u30ED\u30D1\u30C6\u30A3\u4E00\u89A7\u3092\u53D6\u5F97\u3057\u307E\u3059\u3002\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u6642\u306B\u30BF\u30FC\u30B2\u30C3\u30C8\u30D7\u30ED\u30D1\u30C6\u30A3\u3092\u9078\u629E\u3059\u308B\u305F\u3081\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002
510
740
 
511
741
  ### Business Logic
512
742
 
@@ -1,5 +1,12 @@
1
1
  import * as _squadbase_connectors_sdk from '@squadbase/connectors/sdk';
2
+ import { GoogleCalendarClientOptions } from '@squadbase/connectors/sdk';
2
3
 
3
- declare const connection: (connectionId: string) => _squadbase_connectors_sdk.GoogleCalendarConnectorSdk;
4
+ /**
5
+ * Create a Google Calendar SDK client for the given connection.
6
+ *
7
+ * @param connectionId - The connection ID from .squadbase/connections.json
8
+ * @param options - Optional configuration (e.g., subject for Domain-wide Delegation)
9
+ */
10
+ declare const connection: (connectionId: string, options?: GoogleCalendarClientOptions) => _squadbase_connectors_sdk.GoogleCalendarConnectorSdk;
4
11
 
5
12
  export { connection };
@@ -75,17 +75,19 @@ function base64url(input) {
75
75
  const buf = typeof input === "string" ? Buffer.from(input) : input;
76
76
  return buf.toString("base64url");
77
77
  }
78
- function buildJwt(clientEmail, privateKey, nowSec) {
78
+ function buildJwt(clientEmail, privateKey, nowSec, subject) {
79
79
  const header = base64url(JSON.stringify({ alg: "RS256", typ: "JWT" }));
80
- const payload = base64url(
81
- JSON.stringify({
82
- iss: clientEmail,
83
- scope: SCOPE,
84
- aud: TOKEN_URL,
85
- iat: nowSec,
86
- exp: nowSec + 3600
87
- })
88
- );
80
+ const claims = {
81
+ iss: clientEmail,
82
+ scope: SCOPE,
83
+ aud: TOKEN_URL,
84
+ iat: nowSec,
85
+ exp: nowSec + 3600
86
+ };
87
+ if (subject) {
88
+ claims.sub = subject;
89
+ }
90
+ const payload = base64url(JSON.stringify(claims));
89
91
  const signingInput = `${header}.${payload}`;
90
92
  const sign = crypto.createSign("RSA-SHA256");
91
93
  sign.update(signingInput);
@@ -93,7 +95,7 @@ function buildJwt(clientEmail, privateKey, nowSec) {
93
95
  const signature = base64url(sign.sign(privateKey));
94
96
  return `${signingInput}.${signature}`;
95
97
  }
96
- function createClient(params) {
98
+ function createClient(params, options) {
97
99
  const serviceAccountKeyJsonBase64 = params[parameters.serviceAccountKeyJsonBase64.slug];
98
100
  const defaultCalendarId = params[parameters.calendarId.slug] ?? "primary";
99
101
  if (!serviceAccountKeyJsonBase64) {
@@ -118,6 +120,7 @@ function createClient(params) {
118
120
  "google-calendar: service account key JSON must contain client_email and private_key"
119
121
  );
120
122
  }
123
+ const subject = options?.subject;
121
124
  let cachedToken = null;
122
125
  let tokenExpiresAt = 0;
123
126
  async function getAccessToken() {
@@ -128,7 +131,8 @@ function createClient(params) {
128
131
  const jwt = buildJwt(
129
132
  serviceAccountKey.client_email,
130
133
  serviceAccountKey.private_key,
131
- nowSec
134
+ nowSec,
135
+ subject
132
136
  );
133
137
  const response = await fetch(TOKEN_URL, {
134
138
  method: "POST",
@@ -177,18 +181,18 @@ function createClient(params) {
177
181
  const data = await response.json();
178
182
  return data.items ?? [];
179
183
  },
180
- async listEvents(options, calendarId) {
184
+ async listEvents(options2, calendarId) {
181
185
  const cid = resolveCalendarId(calendarId);
182
186
  const searchParams = new URLSearchParams();
183
- if (options?.timeMin) searchParams.set("timeMin", options.timeMin);
184
- if (options?.timeMax) searchParams.set("timeMax", options.timeMax);
185
- if (options?.maxResults)
186
- searchParams.set("maxResults", String(options.maxResults));
187
- if (options?.q) searchParams.set("q", options.q);
188
- if (options?.singleEvents != null)
189
- searchParams.set("singleEvents", String(options.singleEvents));
190
- if (options?.orderBy) searchParams.set("orderBy", options.orderBy);
191
- if (options?.pageToken) searchParams.set("pageToken", options.pageToken);
187
+ if (options2?.timeMin) searchParams.set("timeMin", options2.timeMin);
188
+ if (options2?.timeMax) searchParams.set("timeMax", options2.timeMax);
189
+ if (options2?.maxResults)
190
+ searchParams.set("maxResults", String(options2.maxResults));
191
+ if (options2?.q) searchParams.set("q", options2.q);
192
+ if (options2?.singleEvents != null)
193
+ searchParams.set("singleEvents", String(options2.singleEvents));
194
+ if (options2?.orderBy) searchParams.set("orderBy", options2.orderBy);
195
+ if (options2?.pageToken) searchParams.set("pageToken", options2.pageToken);
192
196
  const qs = searchParams.toString();
193
197
  const path2 = `/calendars/${encodeURIComponent(cid)}/events${qs ? `?${qs}` : ""}`;
194
198
  const response = await this.request(path2, { method: "GET" });
@@ -351,7 +355,10 @@ var inputSchema = z.object({
351
355
  "API path appended to https://www.googleapis.com/calendar/v3 (e.g., '/calendars/{calendarId}/events'). {calendarId} is automatically replaced."
352
356
  ),
353
357
  queryParams: z.record(z.string(), z.string()).optional().describe("Query parameters to append to the URL"),
354
- body: z.record(z.string(), z.unknown()).optional().describe("Request body (JSON) for POST/PUT/PATCH methods")
358
+ body: z.record(z.string(), z.unknown()).optional().describe("Request body (JSON) for POST/PUT/PATCH methods"),
359
+ subject: z.string().optional().describe(
360
+ "Email address of the user to impersonate via Domain-wide Delegation. When set, the service account acts on behalf of this user."
361
+ )
355
362
  });
356
363
  var outputSchema = z.discriminatedUnion("success", [
357
364
  z.object({
@@ -371,7 +378,7 @@ Authentication is handled automatically using a service account.
371
378
  {calendarId} in the path is automatically replaced with the connection's default calendar ID.`,
372
379
  inputSchema,
373
380
  outputSchema,
374
- async execute({ connectionId, method, path: path2, queryParams, body }, connections) {
381
+ async execute({ connectionId, method, path: path2, queryParams, body, subject }, connections) {
375
382
  const connection2 = connections.find((c) => c.id === connectionId);
376
383
  if (!connection2) {
377
384
  return {
@@ -394,7 +401,8 @@ Authentication is handled automatically using a service account.
394
401
  scopes: [
395
402
  "https://www.googleapis.com/auth/calendar.readonly",
396
403
  "https://www.googleapis.com/auth/calendar.events.readonly"
397
- ]
404
+ ],
405
+ ...subject ? { clientOptions: { subject } } : {}
398
406
  });
399
407
  const token = await auth.getAccessToken();
400
408
  if (!token) {
@@ -461,18 +469,26 @@ var googleCalendarConnector = new ConnectorPlugin({
461
469
  systemPrompt: {
462
470
  en: `### Tools
463
471
 
464
- - \`google-calendar_request\`: The only way to call the Google Calendar API. Use it to list calendars, get events, and manage calendar data. Authentication is handled automatically using a service account. The {calendarId} placeholder in paths is automatically replaced with the configured default calendar ID.
472
+ - \`google-calendar_request\`: The only way to call the Google Calendar API. Use it to list calendars, get events, and manage calendar data. Authentication is handled automatically using a service account. The {calendarId} placeholder in paths is automatically replaced with the configured default calendar ID. Supports Domain-wide Delegation via the optional \`subject\` parameter to impersonate a user.
465
473
 
466
474
  ### Business Logic
467
475
 
468
476
  The business logic type for this connector is "typescript". Use the connector SDK in your handler. Do NOT read credentials from environment variables.
469
477
 
470
- SDK methods (client created via \`connection(connectionId)\`):
478
+ SDK methods (client created via \`connection(connectionId)\` or \`connection(connectionId, { subject })\`):
471
479
  - \`client.listCalendars()\` \u2014 list all accessible calendars
472
480
  - \`client.listEvents(options?, calendarId?)\` \u2014 list events with optional filters
473
481
  - \`client.getEvent(eventId, calendarId?)\` \u2014 get a single event by ID
474
482
  - \`client.request(path, init?)\` \u2014 low-level authenticated fetch
475
483
 
484
+ #### Domain-wide Delegation
485
+
486
+ To access a user's calendar via Domain-wide Delegation, pass \`subject\` (the user's email) as the second argument to \`connection()\`:
487
+
488
+ \`\`\`ts
489
+ const calendar = connection("<connectionId>", { subject: "user@example.com" });
490
+ \`\`\`
491
+
476
492
  \`\`\`ts
477
493
  import type { Context } from "hono";
478
494
  import { connection } from "@squadbase/vite-server/connectors/google-calendar";
@@ -524,18 +540,26 @@ export default async function handler(c: Context) {
524
540
  - The default calendar ID is "primary" if not configured`,
525
541
  ja: `### \u30C4\u30FC\u30EB
526
542
 
527
- - \`google-calendar_request\`: Google Calendar API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u4E00\u89A7\u53D6\u5F97\u3001\u30A4\u30D9\u30F3\u30C8\u306E\u53D6\u5F97\u3001\u30AB\u30EC\u30F3\u30C0\u30FC\u30C7\u30FC\u30BF\u306E\u7BA1\u7406\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u4F7F\u7528\u3057\u3066\u8A8D\u8A3C\u306F\u81EA\u52D5\u7684\u306B\u51E6\u7406\u3055\u308C\u307E\u3059\u3002\u30D1\u30B9\u5185\u306E{calendarId}\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u306F\u8A2D\u5B9A\u6E08\u307F\u306E\u30C7\u30D5\u30A9\u30EB\u30C8\u30AB\u30EC\u30F3\u30C0\u30FCID\u3067\u81EA\u52D5\u7684\u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002
543
+ - \`google-calendar_request\`: Google Calendar API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u4E00\u89A7\u53D6\u5F97\u3001\u30A4\u30D9\u30F3\u30C8\u306E\u53D6\u5F97\u3001\u30AB\u30EC\u30F3\u30C0\u30FC\u30C7\u30FC\u30BF\u306E\u7BA1\u7406\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u30B5\u30FC\u30D3\u30B9\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u4F7F\u7528\u3057\u3066\u8A8D\u8A3C\u306F\u81EA\u52D5\u7684\u306B\u51E6\u7406\u3055\u308C\u307E\u3059\u3002\u30D1\u30B9\u5185\u306E{calendarId}\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u306F\u8A2D\u5B9A\u6E08\u307F\u306E\u30C7\u30D5\u30A9\u30EB\u30C8\u30AB\u30EC\u30F3\u30C0\u30FCID\u3067\u81EA\u52D5\u7684\u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002Domain-wide Delegation\u3092\u4F7F\u7528\u3059\u308B\u5834\u5408\u3001\u30AA\u30D7\u30B7\u30E7\u30F3\u306E\`subject\`\u30D1\u30E9\u30E1\u30FC\u30BF\u3067\u30E6\u30FC\u30B6\u30FC\u3092\u507D\u88C5\u3067\u304D\u307E\u3059\u3002
528
544
 
529
545
  ### Business Logic
530
546
 
531
547
  \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u30CF\u30F3\u30C9\u30E9\u5185\u3067\u306F\u30B3\u30CD\u30AF\u30BFSDK\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u8A8D\u8A3C\u60C5\u5831\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
532
548
 
533
- SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
549
+ SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u307E\u305F\u306F \`connection(connectionId, { subject })\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
534
550
  - \`client.listCalendars()\` \u2014 \u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u5168\u30AB\u30EC\u30F3\u30C0\u30FC\u306E\u4E00\u89A7\u53D6\u5F97
535
551
  - \`client.listEvents(options?, calendarId?)\` \u2014 \u30D5\u30A3\u30EB\u30BF\u30FC\u4ED8\u304D\u30A4\u30D9\u30F3\u30C8\u4E00\u89A7\u53D6\u5F97
536
552
  - \`client.getEvent(eventId, calendarId?)\` \u2014 ID\u306B\u3088\u308B\u5358\u4E00\u30A4\u30D9\u30F3\u30C8\u53D6\u5F97
537
553
  - \`client.request(path, init?)\` \u2014 \u4F4E\u30EC\u30D9\u30EB\u306E\u8A8D\u8A3C\u4ED8\u304Dfetch
538
554
 
555
+ #### Domain-wide Delegation
556
+
557
+ Domain-wide Delegation\u3067\u30E6\u30FC\u30B6\u30FC\u306E\u30AB\u30EC\u30F3\u30C0\u30FC\u306B\u30A2\u30AF\u30BB\u30B9\u3059\u308B\u306B\u306F\u3001\`connection()\`\u306E\u7B2C2\u5F15\u6570\u306B\`subject\`\uFF08\u30E6\u30FC\u30B6\u30FC\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\uFF09\u3092\u6E21\u3057\u307E\u3059\uFF1A
558
+
559
+ \`\`\`ts
560
+ const calendar = connection("<connectionId>", { subject: "user@example.com" });
561
+ \`\`\`
562
+
539
563
  \`\`\`ts
540
564
  import type { Context } from "hono";
541
565
  import { connection } from "@squadbase/vite-server/connectors/google-calendar";
@@ -649,7 +673,16 @@ function createConnectorSdk(plugin, createClient2) {
649
673
  }
650
674
 
651
675
  // src/connectors/entries/google-calendar.ts
652
- var connection = createConnectorSdk(googleCalendarConnector, createClient);
676
+ var baseConnection = createConnectorSdk(googleCalendarConnector, createClient);
677
+ var connection = (connectionId, options) => {
678
+ if (options) {
679
+ return createConnectorSdk(
680
+ googleCalendarConnector,
681
+ (params) => createClient(params, options)
682
+ )(connectionId);
683
+ }
684
+ return baseConnection(connectionId);
685
+ };
653
686
  export {
654
687
  connection
655
688
  };