@opencomputer/blue 0.0.6 → 0.0.7

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,192 @@
1
+ import { isRecord } from "../protocol.js";
2
+ const GOOGLE_AUTHORIZATION_URL = "https://accounts.google.com/o/oauth2/v2/auth";
3
+ const GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
4
+ const GOOGLE_USERINFO_URL = "https://openidconnect.googleapis.com/v1/userinfo";
5
+ export const GMAIL_SCOPES = [
6
+ "openid",
7
+ "email",
8
+ "https://www.googleapis.com/auth/gmail.modify",
9
+ ];
10
+ function tokenResponse(value) {
11
+ if (!isRecord(value) ||
12
+ typeof value.access_token !== "string" ||
13
+ typeof value.expires_in !== "number") {
14
+ const message = isRecord(value) && typeof value.error_description === "string"
15
+ ? value.error_description
16
+ : isRecord(value) && typeof value.error === "string"
17
+ ? value.error
18
+ : "Google returned an invalid token response";
19
+ throw new Error(message);
20
+ }
21
+ return value;
22
+ }
23
+ async function googleTokenRequest(body) {
24
+ const response = await fetch(GOOGLE_TOKEN_URL, {
25
+ method: "POST",
26
+ headers: { "content-type": "application/x-www-form-urlencoded" },
27
+ body,
28
+ });
29
+ const value = await response.json().catch(() => null);
30
+ if (!response.ok) {
31
+ tokenResponse(value);
32
+ throw new Error(`Google token exchange failed (${String(response.status)})`);
33
+ }
34
+ return tokenResponse(value);
35
+ }
36
+ function credentialFromToken(token, previous) {
37
+ return {
38
+ accessToken: token.access_token,
39
+ refreshToken: token.refresh_token ?? previous?.refreshToken,
40
+ tokenType: token.token_type ?? previous?.tokenType ?? "Bearer",
41
+ expiresAt: Date.now() + token.expires_in * 1_000,
42
+ scopes: token.scope?.split(/\s+/).filter(Boolean) ??
43
+ previous?.scopes ??
44
+ [],
45
+ };
46
+ }
47
+ export function googleAuthorizationUrl(input) {
48
+ const url = new URL(GOOGLE_AUTHORIZATION_URL);
49
+ url.searchParams.set("client_id", input.config.clientId);
50
+ url.searchParams.set("redirect_uri", input.redirectUri);
51
+ url.searchParams.set("response_type", "code");
52
+ url.searchParams.set("access_type", "offline");
53
+ url.searchParams.set("prompt", "consent");
54
+ url.searchParams.set("include_granted_scopes", "true");
55
+ url.searchParams.set("scope", input.scopes.join(" "));
56
+ url.searchParams.set("state", input.state);
57
+ url.searchParams.set("code_challenge", input.codeChallenge);
58
+ url.searchParams.set("code_challenge_method", "S256");
59
+ return url.toString();
60
+ }
61
+ export async function exchangeGoogleCode(input) {
62
+ const body = new URLSearchParams({
63
+ client_id: input.config.clientId,
64
+ code: input.code,
65
+ code_verifier: input.codeVerifier,
66
+ grant_type: "authorization_code",
67
+ redirect_uri: input.redirectUri,
68
+ });
69
+ if (input.config.clientSecret) {
70
+ body.set("client_secret", input.config.clientSecret);
71
+ }
72
+ return credentialFromToken(await googleTokenRequest(body));
73
+ }
74
+ export async function refreshGoogleCredential(config, credential) {
75
+ if (credential.expiresAt > Date.now() + 60_000)
76
+ return credential;
77
+ if (!credential.refreshToken) {
78
+ throw new Error("Google connection has no refresh token");
79
+ }
80
+ const body = new URLSearchParams({
81
+ client_id: config.clientId,
82
+ refresh_token: credential.refreshToken,
83
+ grant_type: "refresh_token",
84
+ });
85
+ if (config.clientSecret) {
86
+ body.set("client_secret", config.clientSecret);
87
+ }
88
+ return credentialFromToken(await googleTokenRequest(body), credential);
89
+ }
90
+ export async function resolveGoogleIdentity(credential) {
91
+ const response = await fetch(GOOGLE_USERINFO_URL, {
92
+ headers: {
93
+ authorization: `${credential.tokenType} ${credential.accessToken}`,
94
+ },
95
+ });
96
+ const value = await response.json().catch(() => null);
97
+ if (!response.ok ||
98
+ !isRecord(value) ||
99
+ typeof value.sub !== "string" ||
100
+ typeof value.email !== "string") {
101
+ throw new Error("Google did not return the connected account identity");
102
+ }
103
+ return {
104
+ id: value.sub,
105
+ email: value.email,
106
+ name: typeof value.name === "string" ? value.name : undefined,
107
+ };
108
+ }
109
+ function googleServiceOrigin(service) {
110
+ switch (service) {
111
+ case "gmail":
112
+ return "https://gmail.googleapis.com";
113
+ case "calendar":
114
+ case "drive":
115
+ case "sheets":
116
+ return "https://www.googleapis.com";
117
+ default:
118
+ throw new Error(`Unsupported Google service: ${service}`);
119
+ }
120
+ }
121
+ async function readLimitedText(response, limit) {
122
+ if (!response.body)
123
+ return "";
124
+ const reader = response.body.getReader();
125
+ const decoder = new TextDecoder();
126
+ let size = 0;
127
+ let body = "";
128
+ try {
129
+ for (;;) {
130
+ const result = await reader.read();
131
+ if (result.done)
132
+ break;
133
+ size += result.value.byteLength;
134
+ if (size > limit) {
135
+ await reader.cancel();
136
+ throw new Error("Google API response exceeded the 4 MB limit");
137
+ }
138
+ body += decoder.decode(result.value, { stream: true });
139
+ }
140
+ return body + decoder.decode();
141
+ }
142
+ finally {
143
+ reader.releaseLock();
144
+ }
145
+ }
146
+ export async function requestGoogleApi(credential, input) {
147
+ if (!input.path.startsWith("/") || input.path.startsWith("//")) {
148
+ throw new Error("Connection request path must be relative");
149
+ }
150
+ const method = (input.method ?? "GET").toUpperCase();
151
+ if (!["GET", "POST", "PUT", "PATCH", "DELETE"].includes(method)) {
152
+ throw new Error(`Unsupported connection request method: ${method}`);
153
+ }
154
+ if (input.body &&
155
+ new TextEncoder().encode(input.body).byteLength > 4_000_000) {
156
+ throw new Error("Connection request body exceeded the 4 MB limit");
157
+ }
158
+ const headers = new Headers();
159
+ for (const [name, value] of Object.entries(input.headers ?? {})) {
160
+ const normalized = name.toLowerCase();
161
+ if (typeof value === "string" &&
162
+ [
163
+ "accept",
164
+ "content-type",
165
+ "if-match",
166
+ "if-none-match",
167
+ ].includes(normalized)) {
168
+ headers.set(name, value);
169
+ }
170
+ }
171
+ headers.set("authorization", `${credential.tokenType} ${credential.accessToken}`);
172
+ const response = await fetch(new URL(input.path, googleServiceOrigin(input.service)), {
173
+ method,
174
+ headers,
175
+ body: method === "GET" || method === "DELETE"
176
+ ? undefined
177
+ : input.body,
178
+ redirect: "error",
179
+ });
180
+ const responseHeaders = {};
181
+ for (const name of ["content-type", "etag", "location"]) {
182
+ const value = response.headers.get(name);
183
+ if (value)
184
+ responseHeaders[name] = value;
185
+ }
186
+ return {
187
+ status: response.status,
188
+ headers: responseHeaders,
189
+ body: await readLimitedText(response, 4_000_000),
190
+ };
191
+ }
192
+ //# sourceMappingURL=google.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/connections/google.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { OAuthCredential } from "./google.js";
2
+ export interface LocalConnection {
3
+ id: string;
4
+ provider: string;
5
+ label: string;
6
+ externalAccountId: string;
7
+ displayName: string;
8
+ scopes: string[];
9
+ clientId: string;
10
+ clientSecret?: string;
11
+ credential: OAuthCredential;
12
+ createdAt: string;
13
+ updatedAt: string;
14
+ }
15
+ export declare function readLocalConnections(path: string): Promise<LocalConnection[]>;
16
+ export declare function writeLocalConnections(path: string, connections: LocalConnection[]): Promise<void>;
@@ -0,0 +1,27 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname } from "node:path";
3
+ export async function readLocalConnections(path) {
4
+ try {
5
+ const value = JSON.parse(await readFile(path, "utf8"));
6
+ if (value &&
7
+ typeof value === "object" &&
8
+ "version" in value &&
9
+ value.version === 1 &&
10
+ "connections" in value &&
11
+ Array.isArray(value.connections)) {
12
+ return value.connections;
13
+ }
14
+ }
15
+ catch {
16
+ // A missing file means no local accounts have been connected.
17
+ }
18
+ return [];
19
+ }
20
+ export async function writeLocalConnections(path, connections) {
21
+ const state = { version: 1, connections };
22
+ await mkdir(dirname(path), { recursive: true });
23
+ await writeFile(path, `${JSON.stringify(state, null, 2)}\n`, {
24
+ mode: 0o600,
25
+ });
26
+ }
27
+ //# sourceMappingURL=local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/connections/local.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuBpC,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAY;IAEZ,IAAI,CAAC;QACH,MAAM,KAAK,GAAY,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QAChE,IACE,KAAK;YACL,OAAO,KAAK,KAAK,QAAQ;YACzB,SAAS,IAAI,KAAK;YAClB,KAAK,CAAC,OAAO,KAAK,CAAC;YACnB,aAAa,IAAI,KAAK;YACtB,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,EAChC,CAAC;YACD,OAAO,KAAK,CAAC,WAAgC,CAAC;QAChD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,8DAA8D;IAChE,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,IAAY,EACZ,WAA8B;IAE9B,MAAM,KAAK,GAAyB,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC;IAChE,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;QAC3D,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;AACL,CAAC"}
@@ -7,4 +7,5 @@ export interface PreparedAgent {
7
7
  export declare function findAgentRoot(startDirectory?: string): Promise<string | undefined>;
8
8
  export declare function prepareAgent(directory: string, outputDirectory?: string): Promise<PreparedAgent>;
9
9
  export declare function initializeAgent(directory: string): Promise<string>;
10
+ export declare function addGmailTools(directory?: string): Promise<string>;
10
11
  export declare function addSlackChannel(directory?: string): Promise<string>;
@@ -52,6 +52,13 @@ export async function prepareAgent(directory, outputDirectory) {
52
52
  recursive: true,
53
53
  });
54
54
  }
55
+ const tools = resolve(agentDirectory, "tools");
56
+ if (await exists(tools)) {
57
+ await mkdir(resolve(runtimeDirectory, ".opencode"), { recursive: true });
58
+ await cp(tools, resolve(runtimeDirectory, ".opencode", "tools"), {
59
+ recursive: true,
60
+ });
61
+ }
55
62
  const workspace = resolve(agentDirectory, "sandbox", "workspace");
56
63
  if (await exists(workspace)) {
57
64
  await cp(workspace, runtimeDirectory, { recursive: true });
@@ -69,6 +76,7 @@ export async function initializeAgent(directory) {
69
76
  throw new Error(`Target already exists: ${root}`);
70
77
  }
71
78
  await mkdir(resolve(root, "agent", "tools"), { recursive: true });
79
+ await mkdir(resolve(root, "agent", "connections"), { recursive: true });
72
80
  await mkdir(resolve(root, "agent", "skills"), { recursive: true });
73
81
  await mkdir(resolve(root, "agent", "channels"), { recursive: true });
74
82
  await mkdir(resolve(root, "agent", "sandbox", "workspace"), {
@@ -97,6 +105,147 @@ You are a concise coding agent. Inspect the workspace before changing files.
97
105
  `);
98
106
  return root;
99
107
  }
108
+ export async function addGmailTools(directory = process.cwd()) {
109
+ const root = await findAgentRoot(directory);
110
+ if (!root) {
111
+ throw new Error("No Blue agent repository was found");
112
+ }
113
+ const toolsDirectory = resolve(root, "agent", "tools");
114
+ const connectionsDirectory = resolve(root, "agent", "connections");
115
+ await mkdir(toolsDirectory, { recursive: true });
116
+ await mkdir(connectionsDirectory, { recursive: true });
117
+ const path = resolve(toolsDirectory, "gmail.ts");
118
+ await writeFileIfMissing(path, `import { tool } from "@opencode-ai/plugin";
119
+
120
+ async function gmail(input: {
121
+ path: string;
122
+ method?: string;
123
+ body?: unknown;
124
+ connection?: string;
125
+ }): Promise<unknown> {
126
+ const base = process.env.BLUE_CONNECTIONS_URL;
127
+ const token = process.env.BLUE_CONNECTION_TOKEN;
128
+ if (!base || !token) throw new Error("Blue connections are unavailable");
129
+ const response = await fetch(\`\${base}/google/fetch\`, {
130
+ method: "POST",
131
+ headers: {
132
+ authorization: \`Bearer \${token}\`,
133
+ "content-type": "application/json",
134
+ },
135
+ body: JSON.stringify({
136
+ service: "gmail",
137
+ label: input.connection,
138
+ method: input.method,
139
+ path: input.path,
140
+ headers: input.body ? { "content-type": "application/json" } : undefined,
141
+ body: input.body ? JSON.stringify(input.body) : undefined,
142
+ }),
143
+ });
144
+ const result = await response.json() as {
145
+ status?: number;
146
+ body?: string;
147
+ error?: { message?: string };
148
+ };
149
+ if (!response.ok) {
150
+ throw new Error(result.error?.message ?? "Blue connection request failed");
151
+ }
152
+ if (!result.status || result.status >= 400) {
153
+ throw new Error(result.body ?? \`Gmail returned \${String(result.status)}\`);
154
+ }
155
+ return result.body ? JSON.parse(result.body) : {};
156
+ }
157
+
158
+ export const search = tool({
159
+ description: "Search Gmail messages using a Gmail search query.",
160
+ args: {
161
+ query: tool.schema.string(),
162
+ maxResults: tool.schema.number().min(1).max(100).default(20),
163
+ connection: tool.schema.string().optional(),
164
+ },
165
+ async execute(args) {
166
+ const query = encodeURIComponent(args.query);
167
+ return JSON.stringify(await gmail({
168
+ path: \`/gmail/v1/users/me/messages?q=\${query}&maxResults=\${args.maxResults}\`,
169
+ connection: args.connection,
170
+ }));
171
+ },
172
+ });
173
+
174
+ export const read = tool({
175
+ description: "Read a Gmail message by message ID.",
176
+ args: {
177
+ messageId: tool.schema.string(),
178
+ connection: tool.schema.string().optional(),
179
+ },
180
+ async execute(args) {
181
+ return JSON.stringify(await gmail({
182
+ path: \`/gmail/v1/users/me/messages/\${encodeURIComponent(args.messageId)}?format=full\`,
183
+ connection: args.connection,
184
+ }));
185
+ },
186
+ });
187
+
188
+ export const modify = tool({
189
+ description: "Add or remove Gmail labels on a message.",
190
+ args: {
191
+ messageId: tool.schema.string(),
192
+ addLabelIds: tool.schema.array(tool.schema.string()).default([]),
193
+ removeLabelIds: tool.schema.array(tool.schema.string()).default([]),
194
+ connection: tool.schema.string().optional(),
195
+ },
196
+ async execute(args) {
197
+ return JSON.stringify(await gmail({
198
+ method: "POST",
199
+ path: \`/gmail/v1/users/me/messages/\${encodeURIComponent(args.messageId)}/modify\`,
200
+ body: {
201
+ addLabelIds: args.addLabelIds,
202
+ removeLabelIds: args.removeLabelIds,
203
+ },
204
+ connection: args.connection,
205
+ }));
206
+ },
207
+ });
208
+
209
+ export const send = tool({
210
+ description: "Send an email through Gmail.",
211
+ args: {
212
+ to: tool.schema.string(),
213
+ subject: tool.schema.string(),
214
+ body: tool.schema.string(),
215
+ connection: tool.schema.string().optional(),
216
+ },
217
+ async execute(args) {
218
+ if (/[\\r\\n]/.test(args.to) || /[\\r\\n]/.test(args.subject)) {
219
+ throw new Error("Email recipients and subjects cannot contain newlines");
220
+ }
221
+ const message = [
222
+ \`To: \${args.to}\`,
223
+ \`Subject: \${args.subject}\`,
224
+ "Content-Type: text/plain; charset=utf-8",
225
+ "",
226
+ args.body,
227
+ ].join("\\r\\n");
228
+ const raw = Buffer.from(message).toString("base64url");
229
+ return JSON.stringify(await gmail({
230
+ method: "POST",
231
+ path: "/gmail/v1/users/me/messages/send",
232
+ body: { raw },
233
+ connection: args.connection,
234
+ }));
235
+ },
236
+ });
237
+ `);
238
+ await writeFileIfMissing(resolve(connectionsDirectory, "google.json"), `${JSON.stringify({
239
+ provider: "google",
240
+ services: ["gmail"],
241
+ scopes: [
242
+ "openid",
243
+ "email",
244
+ "https://www.googleapis.com/auth/gmail.modify",
245
+ ],
246
+ }, null, 2)}\n`);
247
+ return path;
248
+ }
100
249
  export async function addSlackChannel(directory = process.cwd()) {
101
250
  const root = await findAgentRoot(directory);
102
251
  if (!root) {
@@ -1 +1 @@
1
- {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/core/agent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/core/agent.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  export interface BuiltAgentArtifact {
2
2
  agentId: string;
3
3
  channels: string[];
4
+ connections: string[];
4
5
  body: Buffer;
5
6
  digest: string;
6
7
  elapsedMs: number;
@@ -37,10 +37,26 @@ async function collectChannels(agentRoot) {
37
37
  return [];
38
38
  }
39
39
  }
40
+ async function collectConnections(agentRoot) {
41
+ try {
42
+ const entries = await readdir(resolve(agentRoot, "agent", "connections"), {
43
+ withFileTypes: true,
44
+ });
45
+ return entries
46
+ .filter((entry) => entry.isFile())
47
+ .map((entry) => entry.name.replace(/\.[^.]+$/, "").toLowerCase())
48
+ .filter(Boolean)
49
+ .sort();
50
+ }
51
+ catch {
52
+ return [];
53
+ }
54
+ }
40
55
  export async function buildAgentArtifact(agentDirectory) {
41
56
  const startedAt = performance.now();
42
57
  const prepared = await prepareAgent(agentDirectory);
43
58
  const channels = await collectChannels(prepared.root);
59
+ const connections = await collectConnections(prepared.root);
44
60
  const body = Buffer.from(JSON.stringify({
45
61
  version: 1,
46
62
  channels,
@@ -56,6 +72,7 @@ export async function buildAgentArtifact(agentDirectory) {
56
72
  return {
57
73
  agentId,
58
74
  channels,
75
+ connections,
59
76
  body,
60
77
  digest,
61
78
  elapsedMs: Math.round(performance.now() - startedAt),
@@ -1 +1 @@
1
- {"version":3,"file":"artifact.js","sourceRoot":"","sources":["../../src/core/artifact.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"artifact.js","sourceRoot":"","sources":["../../src/core/artifact.ts"],"names":[],"mappings":"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"}
@@ -3,8 +3,10 @@ export interface LocalControlPlaneOptions {
3
3
  statePath: string;
4
4
  agentId?: string;
5
5
  slackEnabled?: boolean;
6
+ googleEnabled?: boolean;
6
7
  openRouterApiKey?: string;
7
8
  model: string;
9
+ connectionStatePath?: string;
8
10
  }
9
11
  export interface LocalControlPlane {
10
12
  url: string;
@@ -3,6 +3,8 @@ import { mkdir, readFile, rename, writeFile, } from "node:fs/promises";
3
3
  import { createServer, } from "node:http";
4
4
  import { dirname } from "node:path";
5
5
  import { WebSocket as NodeWebSocket, WebSocketServer, } from "ws";
6
+ import { refreshGoogleCredential, requestGoogleApi, } from "../connections/google.js";
7
+ import { readLocalConnections, writeLocalConnections, } from "../connections/local.js";
6
8
  import { BLUE_PROTOCOL_VERSION, isRecord, } from "../protocol.js";
7
9
  import { renderAgentLanding } from "../landing/agent.js";
8
10
  const EVENT_TYPES = new Set([
@@ -479,6 +481,7 @@ export async function startLocalControlPlane(options) {
479
481
  agentId: options.agentId ?? "local-agent",
480
482
  mode: "local",
481
483
  slackEnabled: options.slackEnabled ?? false,
484
+ googleEnabled: options.googleEnabled ?? false,
482
485
  });
483
486
  response.writeHead(200, {
484
487
  "content-type": "text/html; charset=utf-8",
@@ -556,6 +559,69 @@ export async function startLocalControlPlane(options) {
556
559
  return;
557
560
  }
558
561
  const suffix = match[2] ?? "";
562
+ const connectionMatch = suffix.match(/^\/connections\/([^/]+)\/fetch$/);
563
+ if (request.method === "POST" &&
564
+ connectionMatch?.[1]) {
565
+ const authorization = request.headers.authorization;
566
+ const token = typeof authorization === "string" &&
567
+ authorization.startsWith("Bearer ")
568
+ ? authorization.slice(7)
569
+ : null;
570
+ if (!token || !safeEqual(token, session.stored.runtimeToken)) {
571
+ sendJson(response, 401, { message: "Runtime token is invalid" });
572
+ return;
573
+ }
574
+ if (!options.connectionStatePath) {
575
+ sendJson(response, 404, {
576
+ message: "No local connections are configured",
577
+ });
578
+ return;
579
+ }
580
+ const body = await readJson(request);
581
+ const input = typeof body.service === "string" && typeof body.path === "string"
582
+ ? {
583
+ service: body.service,
584
+ label: typeof body.label === "string" ? body.label : undefined,
585
+ method: typeof body.method === "string" ? body.method : undefined,
586
+ path: body.path,
587
+ headers: body.headers &&
588
+ typeof body.headers === "object" &&
589
+ !Array.isArray(body.headers)
590
+ ? body.headers
591
+ : undefined,
592
+ body: typeof body.body === "string" ? body.body : undefined,
593
+ }
594
+ : null;
595
+ if (!input) {
596
+ sendJson(response, 400, { message: "Invalid connection request" });
597
+ return;
598
+ }
599
+ const provider = decodeURIComponent(connectionMatch[1]);
600
+ const connections = await readLocalConnections(options.connectionStatePath);
601
+ const index = connections.findIndex((connection) => connection.provider === provider &&
602
+ (!input.label || connection.label === input.label));
603
+ const connection = connections[index];
604
+ if (provider !== "google" || !connection) {
605
+ sendJson(response, 404, {
606
+ message: `No ${provider} connection is available locally`,
607
+ });
608
+ return;
609
+ }
610
+ const credential = await refreshGoogleCredential({
611
+ clientId: connection.clientId,
612
+ clientSecret: connection.clientSecret,
613
+ }, connection.credential);
614
+ if (credential !== connection.credential) {
615
+ connections[index] = {
616
+ ...connection,
617
+ credential,
618
+ updatedAt: new Date().toISOString(),
619
+ };
620
+ await writeLocalConnections(options.connectionStatePath, connections);
621
+ }
622
+ sendJson(response, 200, await requestGoogleApi(credential, input));
623
+ return;
624
+ }
559
625
  if (request.method === "GET" && suffix === "") {
560
626
  sendJson(response, 200, session.snapshot());
561
627
  return;