From 9c4fd9ac20135c5f01e3a6cae3de909db7ed352b Mon Sep 17 00:00:00 2001 From: lvndry Date: Tue, 20 Jan 2026 10:38:49 +0100 Subject: [PATCH] chore: reduce bundle size by 50% --- .gitignore | 1 + bun.lock | 10 ++++-- eslint.config.mjs | 2 +- package.json | 6 ++-- scripts/build.ts | 59 +++++++++++++++++++++++++++++++++++ scripts/debug-keys.mjs | 26 --------------- scripts/debug-parser.mjs | 48 ---------------------------- src/entry.ts | 16 ++++++++++ src/main.ts | 1 - src/services/calendar.test.ts | 8 ++--- src/services/calendar.ts | 32 +++++++++---------- src/services/gmail.test.ts | 8 ++--- src/services/gmail.ts | 40 ++++++++++++------------ tsconfig.build.json | 9 ++++++ 14 files changed, 141 insertions(+), 125 deletions(-) create mode 100644 scripts/build.ts delete mode 100644 scripts/debug-keys.mjs delete mode 100644 scripts/debug-parser.mjs create mode 100644 src/entry.ts create mode 100644 tsconfig.build.json diff --git a/.gitignore b/.gitignore index 849455d..da6dd61 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ jazz.config.*.json # IDE CLAUDE.md .kiro/ +.agent/ diff --git a/bun.lock b/bun.lock index 4238ad8..44b8463 100644 --- a/bun.lock +++ b/bun.lock @@ -18,6 +18,8 @@ "@effect/platform": "^0.90.10", "@effect/platform-node": "^0.96.1", "@effect/workflow": "^0.9.6", + "@googleapis/calendar": "^14.2.0", + "@googleapis/gmail": "^16.1.1", "@inkjs/ui": "^2.0.0", "@inquirer/prompts": "^8.2.0", "@modelcontextprotocol/sdk": "^1.25.2", @@ -31,7 +33,7 @@ "exa-js": "^2.0.12", "fast-glob": "^3.3.3", "fs-extra": "^11.3.3", - "googleapis": "^159.0.0", + "googleapis-common": "^8.0.1", "gray-matter": "^4.0.3", "ink": "^6.6.0", "ink-big-text": "^2.0.0", @@ -140,6 +142,10 @@ "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], + "@googleapis/calendar": ["@googleapis/calendar@14.2.0", "", { "dependencies": { "googleapis-common": "^8.0.0" } }, "sha512-oo6bikN3dl7n43vvknpPzJlCD8FZbs82h4bZLfDAx1wWRUkIusg6++RFQvOKF46rNXsUePqyVwnFx+8oOOyK1A=="], + + "@googleapis/gmail": ["@googleapis/gmail@16.1.1", "", { "dependencies": { "googleapis-common": "^8.0.0" } }, "sha512-XFMMJvhpEoKzwBOHYrcbBu/jHS+8Aer5YmbMUsrZeJgXAbTD6K7K6+/1RJczXIqDoBzVDdECm4WtjAcvEHPLVg=="], + "@hono/node-server": ["@hono/node-server@1.19.9", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw=="], "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], @@ -680,8 +686,6 @@ "google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="], - "googleapis": ["googleapis@159.0.0", "", { "dependencies": { "google-auth-library": "^10.2.0", "googleapis-common": "^8.0.0" } }, "sha512-halby2+lQwHNxUDk70aQNXP1BlBwdwr7svTJZvDi7vKwrWbVMKhVrZ86h8p3zRcWbO4qAgLQ4ODAf8TgD3DhGQ=="], - "googleapis-common": ["googleapis-common@8.0.1", "", { "dependencies": { "extend": "^3.0.2", "gaxios": "^7.0.0-rc.4", "google-auth-library": "^10.1.0", "qs": "^6.7.0", "url-template": "^2.0.8" } }, "sha512-eCzNACUXPb1PW5l0ULTzMHaL/ltPRADoPgjBlT8jWsTbxkCp6siv+qKJ/1ldaybCthGwsYFYallF7u9AkU4L+A=="], "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], diff --git a/eslint.config.mjs b/eslint.config.mjs index a7df74a..979e83a 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -31,7 +31,7 @@ export default [ ignores: ["**/*.test.ts"], languageOptions: { parserOptions: { - project: "./tsconfig.json", + project: ["./tsconfig.json", "./tsconfig.build.json"], tsconfigRootDir, }, globals: { diff --git a/package.json b/package.json index 61a3f9b..3a0bba3 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "url": "https://github.com/lvndry/jazz/issues" }, "scripts": { - "build": "bun build src/main.ts --outfile dist/main.js --target node --minify && tsc --emitDeclarationOnly", + "build": "bun run scripts/build.ts", "typecheck": "bun run tsc --noEmit", "dev": "bun --watch src/main.ts", "start": "bun dist/main.js", @@ -74,6 +74,8 @@ "@effect/platform": "^0.90.10", "@effect/platform-node": "^0.96.1", "@effect/workflow": "^0.9.6", + "@googleapis/calendar": "^14.2.0", + "@googleapis/gmail": "^16.1.1", "@inkjs/ui": "^2.0.0", "@inquirer/prompts": "^8.2.0", "@modelcontextprotocol/sdk": "^1.25.2", @@ -87,7 +89,7 @@ "exa-js": "^2.0.12", "fast-glob": "^3.3.3", "fs-extra": "^11.3.3", - "googleapis": "^159.0.0", + "googleapis-common": "^8.0.1", "gray-matter": "^4.0.3", "ink": "^6.6.0", "ink-big-text": "^2.0.0", diff --git a/scripts/build.ts b/scripts/build.ts new file mode 100644 index 0000000..d4a0fc3 --- /dev/null +++ b/scripts/build.ts @@ -0,0 +1,59 @@ +type SpawnResult = { + readonly exitCode: number | null; + readonly stdout: string; + readonly stderr: string; +}; + +function run(command: string[], opts?: { readonly cwd?: string }): SpawnResult { + const proc = Bun.spawnSync(command, { + ...(opts?.cwd ? { cwd: opts.cwd } : {}), + stdout: "pipe", + stderr: "pipe", + }); + + return { + exitCode: proc.exitCode, + stdout: new TextDecoder().decode(proc.stdout), + stderr: new TextDecoder().decode(proc.stderr), + }; +} + +function main(): void { + const banner = "#!/usr/bin/env node"; + const outfile = "dist/main.js"; + + const buildArgs = [ + "bun", + "build", + "src/entry.ts", + "--outfile", + outfile, + "--target", + "node", + "--minify", + "--external", + "react", + "--external", + "ink", + "--external", + "ink-gradient", + "--external", + "cfonts", + "--external", + "ink-big-text", + "--banner", + banner, + ]; + + const build = run(buildArgs); + if (build.stdout.length > 0) process.stdout.write(build.stdout); + if (build.stderr.length > 0) process.stderr.write(build.stderr); + if (build.exitCode !== 0) throw new Error(`Build failed with exit code ${build.exitCode}`); + + const tsc = run(["bun", "run", "tsc", "--emitDeclarationOnly"]); + if (tsc.stdout.length > 0) process.stdout.write(tsc.stdout); + if (tsc.stderr.length > 0) process.stderr.write(tsc.stderr); + if (tsc.exitCode !== 0) throw new Error(`TypeScript failed with exit code ${tsc.exitCode}`); +} + +main(); diff --git a/scripts/debug-keys.mjs b/scripts/debug-keys.mjs deleted file mode 100644 index 22339d9..0000000 --- a/scripts/debug-keys.mjs +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env node -/* eslint-disable no-control-regex */ -// Run with: node src/cli/ui/debug-keys.mjs -// Then press Option+Left, Option+Right, etc. to see what sequences are sent -// Press Ctrl+C to exit - -process.stdin.setRawMode(true); -process.stdin.resume(); -process.stdin.setEncoding("utf8"); - -console.log("Press keys to see their escape sequences. Ctrl+C to exit.\n"); - -process.stdin.on("data", (key) => { - if (key === "\x03") { - // Ctrl+C - process.exit(); - } - - const hex = [...key].map((c) => c.charCodeAt(0).toString(16).padStart(2, "0")).join(" "); - const escaped = key.replace(/\x1b/g, "ESC").replace(/\r/g, "\\r").replace(/\n/g, "\\n"); - - console.log(`Key: "${escaped}"`); - console.log(`Hex: ${hex}`); - console.log(`Length: ${key.length}`); - console.log("---"); -}); diff --git a/scripts/debug-parser.mjs b/scripts/debug-parser.mjs deleted file mode 100644 index aab4c99..0000000 --- a/scripts/debug-parser.mjs +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env node -/* eslint-disable no-control-regex */ -// Run with: node src/cli/ui/debug-parser.mjs -// Then press Option+Left, Option+Right to see how parseInput handles them - -import { parseInput } from "../src/cli/ui/escape-sequence-parser.js"; - -const mockKey = { - upArrow: false, - downArrow: false, - leftArrow: false, - rightArrow: false, - return: false, - escape: false, - ctrl: false, - shift: false, - tab: false, - backspace: false, - delete: false, - meta: false, -}; - -process.stdin.setRawMode(true); -process.stdin.resume(); -process.stdin.setEncoding("utf8"); - -console.log("Press keys to see how parseInput handles them. Ctrl+C to exit.\n"); - -let escapeBuffer = ""; - -process.stdin.on("data", (input) => { - if (input === "\x03") { - // Ctrl+C - process.exit(); - } - - const hex = [...input].map((c) => c.charCodeAt(0).toString(16).padStart(2, "0")).join(" "); - console.log(`\n--- Input ---`); - console.log(`Raw: "${input.replace(/\x1b/g, "ESC")}"`); - console.log(`Hex: ${hex}`); - console.log(`Buffer before: "${escapeBuffer.replace(/\x1b/g, "ESC")}"`); - - const result = parseInput(input, mockKey, escapeBuffer); - escapeBuffer = result.newBuffer; - - console.log(`Result: ${result.parsed.type}`); - console.log(`Buffer after: "${escapeBuffer.replace(/\x1b/g, "ESC")}"`); -}); diff --git a/src/entry.ts b/src/entry.ts new file mode 100644 index 0000000..ceb8548 --- /dev/null +++ b/src/entry.ts @@ -0,0 +1,16 @@ +/** + * CLI bootstrap entrypoint. + * + * This file runs before the rest of the app is loaded so we can configure the + * Node.js process (e.g. suppress known noisy deprecation warnings) prior to + * importing the main CLI module and its dependency tree. + */ + +// Suppress DeprecationWarning output (including Node's `punycode` warning coming +// from transitive dependencies on newer Node versions). +process.noDeprecation = true; + +void import("./main").catch((error) => { + console.error("Fatal error:", error); + throw error; +}); diff --git a/src/main.ts b/src/main.ts index 934d3ed..7ef094b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,3 @@ - import { Effect } from "effect"; import { createCLIApp } from "./cli/cli-app"; diff --git a/src/services/calendar.test.ts b/src/services/calendar.test.ts index 81c1bd5..708ce53 100644 --- a/src/services/calendar.test.ts +++ b/src/services/calendar.test.ts @@ -1,13 +1,12 @@ import { FileSystem } from "@effect/platform"; +import { auth, type calendar_v3 } from "@googleapis/calendar"; import { beforeEach, describe, expect, it, mock } from "bun:test"; import { Effect, Either } from "effect"; -import { google, type calendar_v3 } from "googleapis"; import { CalendarServiceResource } from "./calendar"; import { CalendarService } from "../core/interfaces/calendar"; import type { TerminalService } from "../core/interfaces/terminal"; import { CalendarAuthenticationError, CalendarOperationError } from "../core/types"; - // Helper constant for test tokens with required scopes const TEST_TOKEN_WITH_SCOPES = JSON.stringify({ access_token: "test", @@ -18,7 +17,7 @@ const TEST_TOKEN_WITH_SCOPES = JSON.stringify({ describe("CalendarService", () => { let mockFileSystem: FileSystem.FileSystem; let mockCalendar: calendar_v3.Calendar; - let mockOAuthClient: InstanceType; + let mockOAuthClient: InstanceType; let mockRequireCredentials: () => Effect.Effect; let mockTerminal: TerminalService; let calendarService: CalendarService; @@ -45,7 +44,7 @@ describe("CalendarService", () => { get credentials() { return credentials; }, - } as unknown as InstanceType; + } as unknown as InstanceType; // Mock Calendar API client mockCalendar = { @@ -76,6 +75,7 @@ describe("CalendarService", () => { debug: mock(() => Effect.void), heading: mock(() => Effect.void), list: mock(() => Effect.void), + updateLog: mock(() => Effect.void), ask: mock(() => Effect.succeed("")), password: mock(() => Effect.succeed("")), select: mock(() => Effect.succeed("")), diff --git a/src/services/calendar.ts b/src/services/calendar.ts index 72c404a..5a973e2 100644 --- a/src/services/calendar.ts +++ b/src/services/calendar.ts @@ -1,7 +1,7 @@ import http from "node:http"; import { FileSystem } from "@effect/platform"; +import { calendar, auth, type calendar_v3 } from "@googleapis/calendar"; import { Effect, Layer } from "effect"; -import { google, type calendar_v3 } from "googleapis"; import open from "open"; import { AgentConfigServiceTag, type AgentConfigService } from "@/core/interfaces/agent-config"; import { CalendarServiceTag, type CalendarService } from "@/core/interfaces/calendar"; @@ -39,8 +39,8 @@ export class CalendarServiceResource implements CalendarService { constructor( private readonly fs: FileSystem.FileSystem, private readonly tokenFilePath: string, - private oauthClient: InstanceType, - private calendar: calendar_v3.Calendar, + private oauthClient: InstanceType, + private calendarClient: calendar_v3.Calendar, private readonly requireCredentials: () => Effect.Effect, private readonly terminal: TerminalService, ) {} @@ -100,7 +100,7 @@ export class CalendarServiceResource implements CalendarService { ...(options?.updatedMin && { updatedMin: options.updatedMin }), }; const response = yield* this.wrapCalendarCall( - () => this.calendar.events.list(params), + () => this.calendarClient.events.list(params), "Failed to list events", ); const events = (response.data.items || []).map((item) => @@ -119,7 +119,7 @@ export class CalendarServiceResource implements CalendarService { function* (this: CalendarServiceResource) { yield* this.ensureAuthenticated(); const response = yield* this.wrapCalendarCall( - () => this.calendar.events.get({ calendarId, eventId }), + () => this.calendarClient.events.get({ calendarId, eventId }), "Failed to get event", ); return this.parseEventToCalendarEvent(response.data); @@ -138,7 +138,7 @@ export class CalendarServiceResource implements CalendarService { const requestBody = this.buildEventRequestBody(event); const response = yield* this.wrapCalendarCall( () => - this.calendar.events.insert({ + this.calendarClient.events.insert({ calendarId, requestBody, ...(options?.sendNotifications !== undefined && { @@ -170,7 +170,7 @@ export class CalendarServiceResource implements CalendarService { const requestBody = this.buildEventRequestBody(event); const response = yield* this.wrapCalendarCall( () => - this.calendar.events.patch({ + this.calendarClient.events.patch({ calendarId, eventId, requestBody, @@ -201,7 +201,7 @@ export class CalendarServiceResource implements CalendarService { yield* this.ensureAuthenticated(); yield* this.wrapCalendarCall( () => - this.calendar.events.delete({ + this.calendarClient.events.delete({ calendarId, eventId, ...(sendNotifications !== undefined && { sendNotifications }), @@ -221,7 +221,7 @@ export class CalendarServiceResource implements CalendarService { function* (this: CalendarServiceResource) { yield* this.ensureAuthenticated(); const response = yield* this.wrapCalendarCall( - () => this.calendar.calendarList.list(), + () => this.calendarClient.calendarList.list(), "Failed to list calendars", ); const calendars = (response.data.items || []).map((item) => @@ -239,7 +239,7 @@ export class CalendarServiceResource implements CalendarService { function* (this: CalendarServiceResource) { yield* this.ensureAuthenticated(); const response = yield* this.wrapCalendarCall( - () => this.calendar.calendarList.get({ calendarId }), + () => this.calendarClient.calendarList.get({ calendarId }), "Failed to get calendar", ); return this.parseCalendarToCalendarInfo(response.data); @@ -270,7 +270,7 @@ export class CalendarServiceResource implements CalendarService { yield* this.ensureAuthenticated(); const response = yield* this.wrapCalendarCall( () => - this.calendar.events.quickAdd({ + this.calendarClient.events.quickAdd({ calendarId, text, ...(sendNotifications !== undefined && { sendNotifications }), @@ -390,14 +390,14 @@ export class CalendarServiceResource implements CalendarService { if (!clientSecret) { throw new CalendarAuthenticationError({ message: "Missing client secret" }); } - const freshClient = new google.auth.OAuth2({ + const freshClient = new auth.OAuth2({ clientId, clientSecret, redirectUri, }); freshClient.setCredentials(currentCreds); this.oauthClient = freshClient; - this.calendar = google.calendar({ version: "v3", auth: this.oauthClient }); + this.calendarClient = calendar({ version: "v3", auth: this.oauthClient }); // Include both Calendar and Gmail scopes since they share the same token file const scopes = [...ALL_GOOGLE_SCOPES]; @@ -719,8 +719,8 @@ export function createCalendarServiceLayer(): Layer.Layer< const port = getGoogleOAuthPort(); const redirectUri = getGoogleOAuthRedirectUri(port); - const oauth2Client = new google.auth.OAuth2(clientId, clientSecret, redirectUri); - const calendar = google.calendar({ version: "v3", auth: oauth2Client }); + const oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUri); + const calendarInstance = calendar({ version: "v3", auth: oauth2Client }); const { storage } = yield* agentConfig.appConfig; const dataDir = resolveStorageDirectory(storage); @@ -729,7 +729,7 @@ export function createCalendarServiceLayer(): Layer.Layer< fs, tokenFilePath, oauth2Client, - calendar, + calendarInstance, requireCredentials, terminal, ); diff --git a/src/services/gmail.test.ts b/src/services/gmail.test.ts index 748afe8..6f876b3 100644 --- a/src/services/gmail.test.ts +++ b/src/services/gmail.test.ts @@ -1,13 +1,12 @@ import { FileSystem } from "@effect/platform"; +import { auth, type gmail_v1 } from "@googleapis/gmail"; import { beforeEach, describe, expect, it, mock } from "bun:test"; import { Effect, Either } from "effect"; -import { google, type gmail_v1 } from "googleapis"; import { GmailServiceResource } from "./gmail"; import { GmailService } from "../core/interfaces/gmail"; import type { TerminalService } from "../core/interfaces/terminal"; import { GmailAuthenticationError, GmailOperationError } from "../core/types"; - // Helper constant for test tokens with required scopes const TEST_TOKEN_WITH_SCOPES = JSON.stringify({ access_token: "test", @@ -18,7 +17,7 @@ const TEST_TOKEN_WITH_SCOPES = JSON.stringify({ describe("GmailService", () => { let mockFileSystem: FileSystem.FileSystem; let mockGmail: gmail_v1.Gmail; - let mockOAuthClient: InstanceType; + let mockOAuthClient: InstanceType; let mockRequireCredentials: () => Effect.Effect; let mockTerminal: TerminalService; let gmailService: GmailService; @@ -45,7 +44,7 @@ describe("GmailService", () => { get credentials() { return credentials; }, - } as unknown as InstanceType; + } as unknown as InstanceType; // Mock Gmail API client mockGmail = { @@ -84,6 +83,7 @@ describe("GmailService", () => { heading: mock(() => Effect.void), list: mock(() => Effect.void), clear: mock(() => Effect.void), + updateLog: mock(() => Effect.void), ask: mock(() => Effect.succeed("")), password: mock(() => Effect.succeed("")), select: mock(() => Effect.succeed("")), diff --git a/src/services/gmail.ts b/src/services/gmail.ts index d464242..794064f 100644 --- a/src/services/gmail.ts +++ b/src/services/gmail.ts @@ -1,7 +1,7 @@ import http from "node:http"; import { FileSystem } from "@effect/platform"; +import { gmail, auth, type gmail_v1 } from "@googleapis/gmail"; import { Effect, Layer } from "effect"; -import { google, type gmail_v1 } from "googleapis"; import open from "open"; import { AgentConfigServiceTag, type AgentConfigService } from "@/core/interfaces/agent-config"; import { GmailServiceTag, type GmailService } from "@/core/interfaces/gmail"; @@ -30,8 +30,8 @@ export class GmailServiceResource implements GmailService { constructor( private readonly fs: FileSystem.FileSystem, private readonly tokenFilePath: string, - private oauthClient: InstanceType, - private gmail: gmail_v1.Gmail, + private oauthClient: InstanceType, + private gmailClient: gmail_v1.Gmail, private readonly requireCredentials: () => Effect.Effect, private readonly terminal: TerminalService, ) {} @@ -84,7 +84,7 @@ export class GmailServiceResource implements GmailService { ? { userId: "me", maxResults, q: query } : { userId: "me", maxResults }; const listResp = yield* this.wrapGmailCall( - () => this.gmail.users.messages.list(paramsList), + () => this.gmailClient.users.messages.list(paramsList), "Failed to list emails", ); const messages = listResp.data.messages || []; @@ -99,7 +99,7 @@ export class GmailServiceResource implements GmailService { metadataHeaders: ["Subject", "From", "To", "Date", "Cc", "Bcc"], }; const full = yield* this.wrapGmailCall( - () => this.gmail.users.messages.get(paramsGet), + () => this.gmailClient.users.messages.get(paramsGet), "Failed to fetch email metadata", ); const email = this.parseMessageToEmail(full.data); @@ -122,7 +122,7 @@ export class GmailServiceResource implements GmailService { format: "full", }; const full = yield* this.wrapGmailCall( - () => this.gmail.users.messages.get(paramsGet), + () => this.gmailClient.users.messages.get(paramsGet), "Failed to get email", ); const email = this.parseMessageToEmail(full.data, true); @@ -158,7 +158,7 @@ export class GmailServiceResource implements GmailService { // Attachments not implemented in this first pass yield* this.wrapGmailCall( () => - this.gmail.users.drafts.create({ + this.gmailClient.users.drafts.create({ userId: "me", requestBody: { message: { raw } }, }), @@ -182,7 +182,7 @@ export class GmailServiceResource implements GmailService { function* (this: GmailServiceResource) { yield* this.ensureAuthenticated(); const response = yield* this.wrapGmailCall( - () => this.gmail.users.labels.list({ userId: "me" }), + () => this.gmailClient.users.labels.list({ userId: "me" }), "Failed to list labels", ); const labels = (response.data.labels || []).map((label) => @@ -215,7 +215,7 @@ export class GmailServiceResource implements GmailService { ...(options?.color && { color: options.color }), }; const response = yield* this.wrapGmailCall( - () => this.gmail.users.labels.create({ userId: "me", requestBody }), + () => this.gmailClient.users.labels.create({ userId: "me", requestBody }), "Failed to create label", ); return this.parseLabelToGmailLabel(response.data); @@ -245,7 +245,7 @@ export class GmailServiceResource implements GmailService { const response = yield* this.wrapGmailCall( () => - this.gmail.users.labels.update({ + this.gmailClient.users.labels.update({ userId: "me", id: labelId, requestBody, @@ -264,7 +264,7 @@ export class GmailServiceResource implements GmailService { function* (this: GmailServiceResource) { yield* this.ensureAuthenticated(); yield* this.wrapGmailCall( - () => this.gmail.users.labels.delete({ userId: "me", id: labelId }), + () => this.gmailClient.users.labels.delete({ userId: "me", id: labelId }), "Failed to delete label", ); return void 0; @@ -289,7 +289,7 @@ export class GmailServiceResource implements GmailService { const response = yield* this.wrapGmailCall( () => - this.gmail.users.messages.modify({ + this.gmailClient.users.messages.modify({ userId: "me", id: emailId, requestBody, @@ -319,7 +319,7 @@ export class GmailServiceResource implements GmailService { yield* this.wrapGmailCall( () => - this.gmail.users.messages.batchModify({ + this.gmailClient.users.messages.batchModify({ userId: "me", requestBody, }), @@ -335,7 +335,7 @@ export class GmailServiceResource implements GmailService { function* (this: GmailServiceResource) { yield* this.ensureAuthenticated(); yield* this.wrapGmailCall( - () => this.gmail.users.messages.trash({ userId: "me", id: emailId }), + () => this.gmailClient.users.messages.trash({ userId: "me", id: emailId }), "Failed to trash email", ); return void 0; @@ -350,7 +350,7 @@ export class GmailServiceResource implements GmailService { function* (this: GmailServiceResource) { yield* this.ensureAuthenticated(); yield* this.wrapGmailCall( - () => this.gmail.users.messages.delete({ userId: "me", id: emailId }), + () => this.gmailClient.users.messages.delete({ userId: "me", id: emailId }), "Failed to delete email", ); return void 0; @@ -465,14 +465,14 @@ export class GmailServiceResource implements GmailService { if (!clientSecret) { throw new GmailAuthenticationError({ message: "Missing client secret" }); } - const freshClient = new google.auth.OAuth2({ + const freshClient = new auth.OAuth2({ clientId, clientSecret, redirectUri, }); freshClient.setCredentials(currentCreds); this.oauthClient = freshClient; - this.gmail = google.gmail({ version: "v1", auth: this.oauthClient }); + this.gmailClient = gmail({ version: "v1", auth: this.oauthClient }); // Include both Gmail and Calendar scopes since they share the same token file const scopes = ALL_GOOGLE_SCOPES; @@ -720,8 +720,8 @@ export function createGmailServiceLayer(): Layer.Layer< const port = getGoogleOAuthPort(); const redirectUri = getGoogleOAuthRedirectUri(port); - const oauth2Client = new google.auth.OAuth2(clientId, clientSecret, redirectUri); - const gmail = google.gmail({ version: "v1", auth: oauth2Client }); + const oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUri); + const gmailInstance = gmail({ version: "v1", auth: oauth2Client }); const { storage } = yield* agentConfig.appConfig; const dataDir = resolveStorageDirectory(storage); @@ -730,7 +730,7 @@ export function createGmailServiceLayer(): Layer.Layer< fs, tokenFilePath, oauth2Client, - gmail, + gmailInstance, requireCredentials, terminal, ); diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..822569b --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true + }, + "include": ["scripts/**/*.ts"], + "exclude": ["node_modules", "dist"] +}