{"version":3,"file":"index.cjs","names":[],"sources":["../../src/google/host.ts","../../src/google/datetime.ts","../../src/google/paths.ts","../../src/google/util.ts","../../src/google/clientSecret.ts","../../src/google/fsJson.ts","../../src/google/tokenStore.ts","../../src/google/fetch.ts","../../src/google/auth.ts","../../src/google/authFlow.ts","../../src/google/calendar.ts"],"sourcesContent":["// Host binding for the Google engine. The engine logs through the host's\n// logger, but a package-level import of the host logger would be an uphill\n// dependency — so the host injects it once at startup (same pattern as\n// `collection/server/host.ts`). The default is silent so the engine works\n// unconfigured in unit tests.\n\nexport interface GoogleLogger {\n  error: (prefix: string, message: string, data?: Record<string, unknown>) => void;\n  warn: (prefix: string, message: string, data?: Record<string, unknown>) => void;\n  info: (prefix: string, message: string, data?: Record<string, unknown>) => void;\n  debug: (prefix: string, message: string, data?: Record<string, unknown>) => void;\n}\n\nconst silentLogger: GoogleLogger = {\n  error: () => undefined,\n  warn: () => undefined,\n  info: () => undefined,\n  debug: () => undefined,\n};\n\nlet hostLog: GoogleLogger = silentLogger;\n\nexport function configureGoogleHost(binding: { log: GoogleLogger }): void {\n  hostLog = binding.log;\n}\n\nexport const log: GoogleLogger = {\n  error: (prefix, message, data) => hostLog.error(prefix, message, data),\n  warn: (prefix, message, data) => hostLog.warn(prefix, message, data),\n  info: (prefix, message, data) => hostLog.info(prefix, message, data),\n  debug: (prefix, message, data) => hostLog.debug(prefix, message, data),\n};\n","// Strict RFC3339 date-time validation shared by every surface that feeds\n// Calendar `dateTime`/`timeMin` values (agent tool args, remote-host command\n// params). Calendar rejects date-only / offset-less values with an opaque\n// 400, so callers validate here and return an actionable message instead.\n\n// Fractional seconds are normalized away first — an optional `(\\.\\d+)?`\n// group inside the main pattern trips security/detect-unsafe-regex.\nconst ISO_DATE_TIME_WITH_OFFSET_RE = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:Z|[+-](\\d{2}):(\\d{2}))$/;\nconst FRACTIONAL_SECONDS_RE = /\\.\\d+(?=Z|[+-])/;\n\nconst MAX_HOUR = 23;\nconst MAX_MINUTE = 59;\nconst MAX_SECOND = 59;\n\n// JS Date normalizes overflowed components (2026-02-31 parses as\n// 2026-03-03), so `new Date(value)` alone cannot reject impossible dates —\n// a UTC round-trip of the raw components can.\nconst isRealCalendarDate = (year: number, month: number, day: number): boolean => {\n  const roundTrip = new Date(Date.UTC(year, month - 1, day));\n  return roundTrip.getUTCFullYear() === year && roundTrip.getUTCMonth() === month - 1 && roundTrip.getUTCDate() === day;\n};\n\nexport const isIsoDateTimeWithOffset = (value: string): boolean => {\n  const match = ISO_DATE_TIME_WITH_OFFSET_RE.exec(value.replace(FRACTIONAL_SECONDS_RE, \"\"));\n  if (!match) return false;\n  const [year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0] = match.slice(1, 7).map(Number);\n  const timeInRange = hour <= MAX_HOUR && minute <= MAX_MINUTE && second <= MAX_SECOND;\n  // RFC3339 bounds the offset to 00-23:59; groups 7/8 are undefined for \"Z\".\n  const offsetInRange = match[7] === undefined || (Number(match[7]) <= MAX_HOUR && Number(match[8]) <= MAX_MINUTE);\n  return isRealCalendarDate(year, month, day) && timeInRange && offsetInRange;\n};\n","// Google OAuth material lives OUTSIDE the workspace: the client secret is\n// machine-only (never synced) and the refresh token must survive workspace\n// resets — same reasoning as the gcloud / gh CLI model. The dir is the\n// host-NEUTRAL `~/.config/mulmo` because this engine is shared by both\n// MulmoClaude and MulmoTerminal, which deliberately share one grant per\n// machine. The `home` parameter exists so tests can thread a fake home.\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport function googleConfigDir(home?: string): string {\n  return join(home ?? homedir(), \".config\", \"mulmo\");\n}\n\n/** Pre-0.20.1 token dir (mulmoclaude-branded); reads migrate away from it. */\nexport function legacyGoogleTokenPath(home?: string): string {\n  return join(home ?? homedir(), \".config\", \"mulmoclaude\", \"google-token.json\");\n}\n\nexport function googleTokenPath(home?: string): string {\n  return join(googleConfigDir(home), \"google-token.json\");\n}\n\nexport function googleSecretsDir(home?: string): string {\n  return join(home ?? homedir(), \".secrets\");\n}\n","// Small helpers ported from the host (`server/utils/{errors,text,time,types}.ts`)\n// so the Google engine carries no dependency on host utils — same convention\n// as `collection/registry/server/fetch.ts`.\n\nexport const ONE_SECOND_MS = 1_000;\nexport const ONE_MINUTE_MS = 60_000;\n\nexport function errorMessage(err: unknown, fallback = \"unknown error\"): string {\n  if (err instanceof Error) return err.message || fallback;\n  const text = String(err);\n  return text === \"\" || text === \"[object Object]\" ? fallback : text;\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Clip a string to at most `max` chars; the ellipsis is included in the\n *  budget so output never exceeds `max`. */\nexport function truncate(text: string, max: number, ellipsis = \"…\"): string {\n  if (max <= 0) return \"\";\n  if (text.length <= max) return text;\n  return `${text.slice(0, Math.max(0, max - ellipsis.length))}${ellipsis}`;\n}\n","// Loads the Google OAuth desktop-app client credentials the user downloaded\n// from the Cloud Console into `~/.secrets/client_secret_*.json`. The file is\n// discovered by prefix so the user doesn't have to rename Google's long\n// default filename.\nimport { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { isRecord } from \"./util.js\";\nimport { googleSecretsDir } from \"./paths.js\";\n\nexport interface InstalledClientSecret {\n  client_id: string;\n  client_secret: string;\n}\n\nconst isInstalledClientSecret = (value: unknown): value is { installed: InstalledClientSecret } => {\n  if (!isRecord(value) || !isRecord(value.installed)) return false;\n  return typeof value.installed.client_id === \"string\" && typeof value.installed.client_secret === \"string\";\n};\n\nconst isClientSecretFileName = (name: string): boolean => name.startsWith(\"client_secret_\") && name.endsWith(\".json\");\n\nasync function listClientSecretFiles(home?: string): Promise<string[]> {\n  const entries = await readdir(googleSecretsDir(home)).catch((): string[] => []);\n  return entries.filter(isClientSecretFileName).sort();\n}\n\n/** \"ambiguous\" (2+ files) is distinct from \"missing\" — the fixes differ\n *  (remove duplicates vs download credentials), so the UI must not conflate\n *  them. */\nexport type ClientSecretPresence = \"found\" | \"missing\" | \"ambiguous\";\n\nexport async function clientSecretPresence(home?: string): Promise<ClientSecretPresence> {\n  const matches = await listClientSecretFiles(home);\n  if (matches.length === 0) return \"missing\";\n  return matches.length === 1 ? \"found\" : \"ambiguous\";\n}\n\nexport async function findClientSecretPath(home?: string): Promise<string> {\n  const dir = googleSecretsDir(home);\n  const matches = await listClientSecretFiles(home);\n  const [first, ...rest] = matches;\n  if (!first) {\n    throw new Error(\n      `no client_secret_*.json found in ${dir} — download the OAuth desktop-app credentials JSON from the Google Cloud Console and place it there (mode 600)`,\n    );\n  }\n  if (rest.length > 0) {\n    // The stored refresh token is bound to one client_id; silently picking one\n    // of several files could pair the token with the wrong client\n    // (invalid_grant), so ambiguity is an error the user must resolve.\n    throw new Error(`multiple client_secret_*.json files found in ${dir} (${matches.join(\", \")}) — keep exactly one`);\n  }\n  return join(dir, first);\n}\n\nexport async function loadClientSecret(home?: string): Promise<InstalledClientSecret> {\n  const filePath = await findClientSecretPath(home);\n  const raw = await readFile(filePath, \"utf-8\");\n  const parsed: unknown = JSON.parse(raw);\n  if (!isInstalledClientSecret(parsed)) {\n    throw new Error(`${filePath} is not a desktop-app OAuth client secret (missing \"installed\" with client_id / client_secret)`);\n  }\n  return { client_id: parsed.installed.client_id, client_secret: parsed.installed.client_secret };\n}\n","// Minimal JSON file I/O for the token store. Unlike the host's\n// `writeJsonAtomic` (and core's collection `atomic.ts`), this one supports a\n// file mode — the token file must be 0600.\nimport { promises as fsp } from \"node:fs\";\nimport path from \"node:path\";\n\nexport async function readJsonOrNull<T>(filePath: string): Promise<T | null> {\n  try {\n    const raw = await fsp.readFile(filePath, \"utf-8\");\n    const parsed: T = JSON.parse(raw);\n    return parsed;\n  } catch {\n    return null;\n  }\n}\n\nconst IS_WINDOWS = process.platform === \"win32\";\nconst RENAME_RETRY_DELAYS_MS = [30, 100, 300];\n\nconst hasErrnoCode = (err: unknown): err is { code: string } =>\n  typeof err === \"object\" && err !== null && \"code\" in err && typeof (err as { code: unknown }).code === \"string\";\n\n// On Windows, AV / Search Indexer / Defender briefly hold handles and rename\n// trips EPERM/EBUSY/EACCES. The retry is gated to Windows because POSIX EPERM\n// means a real permission problem — retrying would just delay the throw.\n// Ported from the host's server/utils/files/atomic.ts.\nconst isTransientRenameError = (err: unknown): boolean =>\n  IS_WINDOWS && hasErrnoCode(err) && (err.code === \"EPERM\" || err.code === \"EBUSY\" || err.code === \"EACCES\");\n\nasync function renameWithWindowsRetry(fromPath: string, toPath: string): Promise<void> {\n  for (const delayMs of RENAME_RETRY_DELAYS_MS) {\n    try {\n      await fsp.rename(fromPath, toPath);\n      return;\n    } catch (err) {\n      if (!isTransientRenameError(err)) throw err;\n      await new Promise((resolve) => setTimeout(resolve, delayMs));\n    }\n  }\n  await fsp.rename(fromPath, toPath);\n}\n\n/** tmp-write + rename so readers never see a half-written file; `mode`\n *  applies to the tmp file and survives the rename. */\nexport async function writeJsonAtomicWithMode(filePath: string, data: unknown, mode: number): Promise<void> {\n  const tmp = `${filePath}.tmp`;\n  await fsp.mkdir(path.dirname(filePath), { recursive: true });\n  try {\n    await fsp.writeFile(tmp, JSON.stringify(data, null, 2), { encoding: \"utf-8\", mode });\n    await renameWithWindowsRetry(tmp, filePath);\n  } catch (err) {\n    await fsp.unlink(tmp).catch(() => undefined);\n    throw err;\n  }\n}\n","// Persistence for the Google OAuth tokens (refresh + access) at\n// `~/.config/mulmo/google-token.json`, mode 600. Google omits\n// `refresh_token` from refresh responses, so merges must preserve the one we\n// already hold — losing it forces the user through the browser consent again.\nimport { constants as fsConstants, copyFile, mkdir, rm, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { Credentials } from \"google-auth-library\";\nimport { readJsonOrNull, writeJsonAtomicWithMode } from \"./fsJson.js\";\nimport { googleTokenPath, legacyGoogleTokenPath } from \"./paths.js\";\n\nconst TOKEN_FILE_MODE = 0o600;\n\nexport function mergeGoogleTokens(existing: Credentials | null, incoming: Credentials): Credentials {\n  const merged = { ...existing, ...incoming };\n  if (!incoming.refresh_token && existing?.refresh_token) merged.refresh_token = existing.refresh_token;\n  return merged;\n}\n\nconst fileExists = async (filePath: string): Promise<boolean> =>\n  await stat(filePath).then(\n    () => true,\n    () => false,\n  );\n\n// Tokens written before 0.20.1 live under the mulmoclaude-branded dir; move\n// them once. COPYFILE_EXCL makes the create atomic-and-non-clobbering — an\n// exists+rename sequence could overwrite a token a concurrent process wrote\n// to the new path in between (TOCTOU). The legacy file is deleted only after\n// a successful copy; on EEXIST (new path won a race, or both files already\n// exist) it is left for any older install still reading it. copyFile\n// preserves the 600 mode.\nasync function migrateLegacyTokenFile(home?: string): Promise<void> {\n  const current = googleTokenPath(home);\n  const legacy = legacyGoogleTokenPath(home);\n  if (!(await fileExists(legacy))) return;\n  await mkdir(path.dirname(current), { recursive: true });\n  try {\n    await copyFile(legacy, current, fsConstants.COPYFILE_EXCL);\n  } catch {\n    return;\n  }\n  await rm(legacy, { force: true });\n}\n\nexport async function loadGoogleTokens(home?: string): Promise<Credentials | null> {\n  await migrateLegacyTokenFile(home).catch(() => undefined);\n  const current = await readJsonOrNull<Credentials>(googleTokenPath(home));\n  if (current) return current;\n  // Migration is best-effort — a valid legacy token must still count as\n  // linked even when the move failed (permissions, read-only fs, …).\n  return await readJsonOrNull<Credentials>(legacyGoogleTokenPath(home));\n}\n\nexport async function saveGoogleTokens(incoming: Credentials, home?: string): Promise<Credentials> {\n  const merged = mergeGoogleTokens(await loadGoogleTokens(home), incoming);\n  await writeJsonAtomicWithMode(googleTokenPath(home), merged, TOKEN_FILE_MODE);\n  return merged;\n}\n\nexport async function deleteGoogleTokens(home?: string): Promise<void> {\n  await rm(googleTokenPath(home), { force: true });\n}\n","// `fetch` with a finite timeout, ported from the host (`server/utils/fetch.ts`)\n// so the Google engine carries no dependency on host utils (same convention as\n// `collection/registry/server/fetch.ts`).\n\nimport { ONE_SECOND_MS } from \"./util.js\";\n\nexport const DEFAULT_FETCH_TIMEOUT_MS = 10 * ONE_SECOND_MS;\n\n// `Parameters<typeof fetch>[1]` avoids referencing the ambient `RequestInit`\n// type, which ESLint's `no-undef` rule trips over in the server config.\nexport type FetchWithTimeoutInit = Parameters<typeof fetch>[1] & { timeoutMs?: number };\n\n/** `fetch` with a finite timeout. Rejects with a `TimeoutError` once\n *  `timeoutMs` elapses. Composes with a caller-supplied `signal` so external\n *  cancellation still works. */\nexport async function fetchWithTimeout(url: string | URL, init: FetchWithTimeoutInit = {}): Promise<Response> {\n  const { timeoutMs = DEFAULT_FETCH_TIMEOUT_MS, signal: callerSignal, ...rest } = init;\n\n  if (callerSignal?.aborted) {\n    throw callerSignal.reason ?? new DOMException(\"Aborted\", \"AbortError\");\n  }\n\n  const controller = new AbortController();\n  const timer = setTimeout(() => {\n    controller.abort(new DOMException(`fetch timed out after ${timeoutMs}ms`, \"TimeoutError\"));\n  }, timeoutMs);\n\n  const unsubscribeCaller = bridgeExternalSignal(callerSignal, controller);\n\n  try {\n    return await fetch(url, { ...rest, signal: controller.signal });\n  } finally {\n    clearTimeout(timer);\n    unsubscribeCaller?.();\n  }\n}\n\nfunction bridgeExternalSignal(external: AbortSignal | null | undefined, controller: AbortController): (() => void) | null {\n  if (!external) return null;\n  const onAbort = () => controller.abort(external.reason);\n  external.addEventListener(\"abort\", onAbort, { once: true });\n  return () => external.removeEventListener(\"abort\", onAbort);\n}\n","// Google OAuth for the host machine, independent of Firebase Auth (which\n// discards refresh tokens). Entry points:\n//   - authorizeGoogle(): one-shot loopback + PKCE browser consent flow\n//     (desktop-app clients may redirect to any 127.0.0.1 port), storing the\n//     refresh token locally via tokenStore.\n//   - getGoogleAccessToken(): mints a fresh access token from the stored\n//     refresh token; the OAuth2Client \"tokens\" event persists rotations.\n//   - unlinkGoogle(): best-effort revoke at Google + local token delete.\nimport { randomBytes } from \"node:crypto\";\nimport http from \"node:http\";\nimport { CodeChallengeMethod, OAuth2Client, type Credentials } from \"google-auth-library\";\nimport { log } from \"./host.js\";\nimport { errorMessage, ONE_MINUTE_MS, ONE_SECOND_MS } from \"./util.js\";\nimport { fetchWithTimeout } from \"./fetch.js\";\nimport { loadClientSecret, type InstalledClientSecret } from \"./clientSecret.js\";\nimport { deleteGoogleTokens, loadGoogleTokens, saveGoogleTokens } from \"./tokenStore.js\";\n\nexport const GOOGLE_CALENDAR_SCOPE = \"https://www.googleapis.com/auth/calendar.events\";\nexport const GOOGLE_TASKS_SCOPE = \"https://www.googleapis.com/auth/tasks\";\nexport const GOOGLE_DRIVE_FILE_SCOPE = \"https://www.googleapis.com/auth/drive.file\";\n/** Requested at consent as one set — matches the scopes registered on the\n *  OAuth consent screen, so a single re-link covers every supported API\n *  (Calendar now; Tasks / Drive tools ride the same grant later). */\nexport const GOOGLE_SCOPES = [GOOGLE_CALENDAR_SCOPE, GOOGLE_TASKS_SCOPE, GOOGLE_DRIVE_FILE_SCOPE];\nconst CALLBACK_PATH = \"/oauth2callback\";\nconst AUTH_TIMEOUT_MS = 5 * ONE_MINUTE_MS;\nconst STATE_BYTES = 16;\n\nexport interface AuthorizeGoogleOptions {\n  home?: string;\n  /** Called with the consent URL; open it in a browser (and/or print it). */\n  onAuthUrl?: (url: string) => void;\n  timeoutMs?: number;\n}\n\nconst createClient = (secret: InstalledClientSecret, redirectUri?: string): OAuth2Client =>\n  new OAuth2Client({ clientId: secret.client_id, clientSecret: secret.client_secret, redirectUri });\n\nconst persistRotatedTokens = (client: OAuth2Client, home?: string): void => {\n  client.on(\"tokens\", (tokens) => {\n    saveGoogleTokens(tokens, home).catch((err: unknown) => {\n      log.error(\"google\", \"failed to persist rotated tokens\", { error: String(err) });\n    });\n  });\n};\n\nexport async function getGoogleAccessToken(home?: string): Promise<string> {\n  const saved = await loadGoogleTokens(home);\n  if (!saved?.refresh_token) {\n    throw new Error(\"Google account not linked on this host — link it in Settings → Plugins → Google, or run `yarn google:auth`\");\n  }\n  const client = createClient(await loadClientSecret(home));\n  client.setCredentials(saved);\n  persistRotatedTokens(client, home);\n  const { token } = await client.getAccessToken();\n  if (!token) {\n    throw new Error(\"could not obtain a Google access token — the grant may have been revoked; re-link the account\");\n  }\n  return token;\n}\n\nconst REVOKE_URL = \"https://oauth2.googleapis.com/revoke\";\n\n/** The revoke POST, injectable for tests. */\nexport type RevokeFetch = typeof fetchWithTimeout;\n\n/** Revoke the grant at Google (best-effort) and delete the local token file.\n *  Revoke failures are logged but never block the local delete — Google may\n *  already consider the token invalid, and keeping the file would leave the\n *  user unable to unlink. */\nexport async function unlinkGoogle(home?: string, revokeFetch: RevokeFetch = fetchWithTimeout): Promise<void> {\n  const saved = await loadGoogleTokens(home);\n  const token = saved?.refresh_token ?? saved?.access_token;\n  if (token) {\n    try {\n      const response = await revokeFetch(REVOKE_URL, {\n        method: \"POST\",\n        headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n        body: new URLSearchParams({ token }).toString(),\n      });\n      if (!response.ok) log.warn(\"google\", \"token revoke returned non-ok\", { status: response.status });\n    } catch (err) {\n      log.warn(\"google\", \"token revoke failed, deleting local tokens anyway\", { error: errorMessage(err) });\n    }\n  }\n  await deleteGoogleTokens(home);\n}\n\nconst startLoopbackServer = (): Promise<{ server: http.Server; port: number }> =>\n  new Promise((resolve, reject) => {\n    const server = http.createServer();\n    server.once(\"error\", reject);\n    server.listen(0, \"127.0.0.1\", () => {\n      const address = server.address();\n      if (address === null || typeof address === \"string\") {\n        reject(new Error(\"loopback server has no port\"));\n        return;\n      }\n      resolve({ server, port: address.port });\n    });\n  });\n\n// State is validated before error/code — a callback that can't prove it\n// belongs to this flow must not influence it (its `error` text would\n// otherwise reach the terminal attacker-controlled).\nconst authCodeFromCallback = (url: URL, expectedState: string): string => {\n  if (url.searchParams.get(\"state\") !== expectedState) throw new Error(\"OAuth state mismatch — possible CSRF, aborting\");\n  const error = url.searchParams.get(\"error\");\n  if (error) throw new Error(`Google authorization failed: ${error}`);\n  const code = url.searchParams.get(\"code\");\n  if (!code) throw new Error(\"authorization callback carried no code\");\n  return code;\n};\n\nconst respondHtml = (res: http.ServerResponse, status: number, message: string): void => {\n  res.writeHead(status, { \"Content-Type\": \"text/html; charset=utf-8\" });\n  res.end(`<html><body><h3>${message}</h3></body></html>`);\n};\n\nexport const waitForAuthCode = (server: http.Server, expectedState: string, timeoutMs: number): Promise<string> =>\n  new Promise((resolve, reject) => {\n    const timer = setTimeout(() => reject(new Error(`authorization timed out after ${timeoutMs / ONE_SECOND_MS}s`)), timeoutMs);\n    server.on(\"request\", (req, res) => {\n      const url = new URL(req.url ?? \"/\", \"http://127.0.0.1\");\n      if (url.pathname !== CALLBACK_PATH) {\n        res.writeHead(404);\n        res.end();\n        return;\n      }\n      // A wrong-state request is not our callback (drive-by localhost probe\n      // or stale tab) — answer it but keep waiting for the real redirect, so\n      // an unauthenticated request can't abort the pending flow.\n      if (url.searchParams.get(\"state\") !== expectedState) {\n        respondHtml(res, 400, \"Invalid authorization callback. You can close this tab.\");\n        return;\n      }\n      clearTimeout(timer);\n      try {\n        const code = authCodeFromCallback(url, expectedState);\n        respondHtml(res, 200, \"Authorization complete — you can close this tab.\");\n        resolve(code);\n      } catch (err) {\n        // Static text only — the failure detail echoes query-string content,\n        // which must not be reflected into HTML. The CLI prints the detail.\n        respondHtml(res, 400, \"Authorization failed — see the terminal for details. You can close this tab.\");\n        reject(err);\n      }\n    });\n  });\n\n// `access_type: offline` + `prompt: consent` force Google to return a refresh\n// token on every run (repeat consents otherwise omit it).\nconst buildConsentUrl = (client: OAuth2Client, codeChallenge: string, state: string): string =>\n  client.generateAuthUrl({\n    access_type: \"offline\",\n    prompt: \"consent\",\n    scope: GOOGLE_SCOPES,\n    code_challenge_method: CodeChallengeMethod.S256,\n    code_challenge: codeChallenge,\n    state,\n  });\n\nexport async function authorizeGoogle(opts: AuthorizeGoogleOptions = {}): Promise<Credentials> {\n  const secret = await loadClientSecret(opts.home);\n  const { server, port } = await startLoopbackServer();\n  try {\n    const client = createClient(secret, `http://127.0.0.1:${port}${CALLBACK_PATH}`);\n    const { codeVerifier, codeChallenge } = await client.generateCodeVerifierAsync();\n    if (!codeChallenge) throw new Error(\"failed to derive a PKCE code challenge\");\n    const state = randomBytes(STATE_BYTES).toString(\"hex\");\n    opts.onAuthUrl?.(buildConsentUrl(client, codeChallenge, state));\n    const code = await waitForAuthCode(server, state, opts.timeoutMs ?? AUTH_TIMEOUT_MS);\n    const { tokens } = await client.getToken({ code, codeVerifier });\n    if (!tokens.refresh_token) {\n      throw new Error(\"Google returned no refresh token — remove this app under Google Account → Security → Third-party access, then retry\");\n    }\n    return await saveGoogleTokens(tokens, opts.home);\n  } finally {\n    server.close();\n  }\n}\n","// In-flight manager for the settings-UI OAuth flow. authorizeGoogle()\n// resolves only after the user finishes the browser consent, so the HTTP\n// layer starts it in the background, returns the consent URL immediately,\n// and reports progress via status polling. One flow at a time — the guard\n// is the in-flight start promise itself (set synchronously before any\n// await), so concurrent authorize requests share one flow instead of\n// spawning parallel loopback listeners.\nimport { log } from \"./host.js\";\nimport { errorMessage } from \"./util.js\";\nimport { authorizeGoogle } from \"./auth.js\";\n\nexport interface GoogleAuthFlowStatus {\n  pending: boolean;\n  lastError: string | null;\n}\n\nexport interface GoogleAuthFlow {\n  start: () => Promise<{ authUrl: string }>;\n  status: () => GoogleAuthFlowStatus;\n}\n\nexport const createGoogleAuthFlow = (authorize: typeof authorizeGoogle): GoogleAuthFlow => {\n  let inFlightStart: Promise<{ authUrl: string }> | null = null;\n  let flowRunning = false;\n  let lastError: string | null = null;\n\n  const launchFlow = (): Promise<{ authUrl: string }> =>\n    new Promise((resolve, reject) => {\n      flowRunning = true;\n      authorize({\n        onAuthUrl: (url) => resolve({ authUrl: url }),\n      })\n        .then(() => log.info(\"google\", \"authorize flow completed\"))\n        .catch((err: unknown) => {\n          lastError = errorMessage(err);\n          log.warn(\"google\", \"authorize flow failed\", { error: lastError });\n          // No-op when the URL already resolved; covers pre-URL failures\n          // (missing client secret, port bind error).\n          reject(err instanceof Error ? err : new Error(String(err)));\n        })\n        .finally(() => {\n          flowRunning = false;\n          inFlightStart = null;\n        });\n    });\n\n  const start = (): Promise<{ authUrl: string }> => {\n    if (inFlightStart) return inFlightStart;\n    lastError = null;\n    inFlightStart = launchFlow();\n    return inFlightStart;\n  };\n\n  const status = (): GoogleAuthFlowStatus => ({ pending: flowRunning, lastError });\n\n  return { start, status };\n};\n\nexport const googleAuthFlow = createGoogleAuthFlow(authorizeGoogle);\n","// Google Calendar v3 REST calls against the user's primary calendar. Plain\n// fetch instead of the `googleapis` SDK — two endpoints don't justify the\n// dependency (see plans/done/feat-google-oauth-calendar.md).\nimport { errorMessage, isRecord, ONE_SECOND_MS, truncate } from \"./util.js\";\nimport { fetchWithTimeout } from \"./fetch.js\";\n\nconst CALENDAR_EVENTS_URL = \"https://www.googleapis.com/calendar/v3/calendars/primary/events\";\nconst CALENDAR_TIMEOUT_MS = 30 * ONE_SECOND_MS;\nconst ERROR_BODY_MAX_CHARS = 300;\nconst HTTP_FORBIDDEN = 403;\nexport const DEFAULT_LIST_MAX_RESULTS = 10;\nexport const MAX_LIST_RESULTS = 50;\n\nexport interface CalendarEventInput {\n  summary: string;\n  startDateTime: string;\n  endDateTime: string;\n  description?: string;\n}\n\nexport interface ListEventsInput {\n  timeMin?: string;\n  maxResults?: number;\n}\n\nexport interface CalendarEventSummary {\n  id: string;\n  summary: string;\n  start: string;\n  end: string;\n  htmlLink: string;\n  status: string;\n}\n\n// All-day events carry `date`, timed events carry `dateTime`.\nconst eventTime = (value: unknown): string => {\n  if (!isRecord(value)) return \"\";\n  if (typeof value.dateTime === \"string\") return value.dateTime;\n  if (typeof value.date === \"string\") return value.date;\n  return \"\";\n};\n\nexport const toEventSummary = (value: unknown): CalendarEventSummary => {\n  const record: Record<string, unknown> = isRecord(value) ? value : {};\n  return {\n    id: typeof record.id === \"string\" ? record.id : \"\",\n    summary: typeof record.summary === \"string\" ? record.summary : \"\",\n    start: eventTime(record.start),\n    end: eventTime(record.end),\n    htmlLink: typeof record.htmlLink === \"string\" ? record.htmlLink : \"\",\n    status: typeof record.status === \"string\" ? record.status : \"\",\n  };\n};\n\nexport const calendarApiError = (status: number, body: string): Error => {\n  const hint = status === HTTP_FORBIDDEN ? \" (is the Google Calendar API enabled for the Cloud project?)\" : \"\";\n  const detail = body ? ` — ${truncate(body, ERROR_BODY_MAX_CHARS)}` : \"\";\n  return new Error(`Google Calendar API: HTTP ${status}${hint}${detail}`);\n};\n\nconst calendarRequest = async (accessToken: string, url: string, init: { method?: string; body?: string } = {}): Promise<unknown> => {\n  const response = await fetchWithTimeout(url, {\n    ...init,\n    timeoutMs: CALENDAR_TIMEOUT_MS,\n    headers: { Authorization: `Bearer ${accessToken}`, \"Content-Type\": \"application/json\" },\n  });\n  if (!response.ok) {\n    const body = await response.text().catch((err: unknown) => errorMessage(err));\n    throw calendarApiError(response.status, body);\n  }\n  return await response.json();\n};\n\nexport async function createCalendarEvent(accessToken: string, input: CalendarEventInput): Promise<CalendarEventSummary> {\n  const body = {\n    summary: input.summary,\n    description: input.description,\n    start: { dateTime: input.startDateTime },\n    end: { dateTime: input.endDateTime },\n  };\n  const created = await calendarRequest(accessToken, CALENDAR_EVENTS_URL, { method: \"POST\", body: JSON.stringify(body) });\n  return toEventSummary(created);\n}\n\nexport async function listCalendarEvents(accessToken: string, input: ListEventsInput = {}): Promise<CalendarEventSummary[]> {\n  const params = new URLSearchParams({\n    timeMin: input.timeMin ?? new Date().toISOString(),\n    maxResults: String(input.maxResults ?? DEFAULT_LIST_MAX_RESULTS),\n    singleEvents: \"true\",\n    orderBy: \"startTime\",\n  });\n  const listed = await calendarRequest(accessToken, `${CALENDAR_EVENTS_URL}?${params.toString()}`);\n  const items = isRecord(listed) && Array.isArray(listed.items) ? listed.items : [];\n  return items.map(toEventSummary);\n}\n"],"mappings":"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"}