{"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/broker.ts","../../src/google/auth.ts","../../src/google/authFlow.ts","../../src/google/apiClient.ts","../../src/google/calendar.ts","../../src/google/tasks.ts","../../src/google/driveFile.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 a user may have\n// downloaded from the Cloud Console into `~/.secrets/client_secret_*.json`.\n// Files are discovered by prefix so nobody has to rename Google's long default\n// filename.\n//\n// Only `{\"installed\": …}` (desktop) files count. A `{\"web\": …}` client cannot\n// drive the loopback consent this engine runs — and one legitimately sits in\n// the same directory for anyone who also deploys the broker — so web clients\n// are skipped rather than treated as a competing choice.\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\n// Empty strings must not count: they would satisfy a `typeof` check, get\n// picked as the client, and fail at Google with an opaque invalid_client.\nconst isNonEmptyString = (value: unknown): value is string => typeof value === \"string\" && value !== \"\";\n\nconst isInstalledClientSecret = (value: unknown): value is { installed: InstalledClientSecret } => {\n  if (!isRecord(value) || !isRecord(value.installed)) return false;\n  return isNonEmptyString(value.installed.client_id) && isNonEmptyString(value.installed.client_secret);\n};\n\nconst isClientSecretFileName = (name: string): boolean => name.startsWith(\"client_secret_\") && name.endsWith(\".json\");\n\nconst readIfDesktopClient = async (filePath: string): Promise<string | null> => {\n  try {\n    const parsed: unknown = JSON.parse(await readFile(filePath, \"utf-8\"));\n    return isInstalledClientSecret(parsed) ? filePath : null;\n  } catch {\n    // Unreadable or malformed: not a usable desktop client, and refusing the\n    // whole directory over one stray file would be worse than ignoring it.\n    return null;\n  }\n};\n\n/** Absolute paths of every desktop-app client JSON in `~/.secrets/`. */\nasync function listDesktopClientSecretFiles(home?: string): Promise<string[]> {\n  const dir = googleSecretsDir(home);\n  const entries = await readdir(dir).catch((): string[] => []);\n  const candidates = entries.filter(isClientSecretFileName).sort();\n  const checked = await Promise.all(candidates.map((name) => readIfDesktopClient(join(dir, name))));\n  return checked.filter((path): path is string => path !== null);\n}\n\n/** `missing` is the ordinary case — the broker supplies the client, so no user\n *  action is needed. `ambiguous` (2+ desktop clients) still needs a human:\n *  a stored refresh token pairs with exactly one client_id, so picking for\n *  them could silently break the link. */\nexport type ClientSecretPresence = \"found\" | \"missing\" | \"ambiguous\";\n\nexport async function clientSecretPresence(home?: string): Promise<ClientSecretPresence> {\n  const matches = await listDesktopClientSecretFiles(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 listDesktopClientSecretFiles(home);\n  const [first, ...rest] = matches;\n  if (!first) {\n    throw new Error(`no desktop-app client_secret_*.json found in ${dir} — this host links through the sign-in service instead`);\n  }\n  if (rest.length > 0) {\n    const names = matches.map((path) => path.slice(dir.length + 1)).join(\", \");\n    throw new Error(`multiple desktop-app client_secret_*.json files found in ${dir} (${names}) — keep exactly one`);\n  }\n  return first;\n}\n\nexport async function loadClientSecret(home?: string): Promise<InstalledClientSecret> {\n  const filePath = await findClientSecretPath(home);\n  const parsed: unknown = JSON.parse(await readFile(filePath, \"utf-8\"));\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\n/** Which client minted the grant: `local` = the user's own client JSON in\n *  `~/.secrets/`, `broker` = the mulmoserver broker. Renewal must use the same\n *  path, since only that client's secret can refresh its tokens. Absent on\n *  tokens written before the broker existed — those are all `local`. */\nexport type IssuedVia = \"local\" | \"broker\";\n\nexport interface StoredGoogleTokens extends Credentials {\n  issuedVia?: IssuedVia;\n}\n\nexport function mergeGoogleTokens(existing: StoredGoogleTokens | null, incoming: StoredGoogleTokens): StoredGoogleTokens {\n  const merged = { ...existing, ...incoming };\n  if (!incoming.refresh_token && existing?.refresh_token) merged.refresh_token = existing.refresh_token;\n  // A broker refresh response carries no marker; keep the one from the link.\n  if (!incoming.issuedVia && existing?.issuedVia) merged.issuedVia = existing.issuedVia;\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<StoredGoogleTokens | null> {\n  await migrateLegacyTokenFile(home).catch(() => undefined);\n  const current = await readJsonOrNull<StoredGoogleTokens>(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<StoredGoogleTokens>(legacyGoogleTokenPath(home));\n}\n\nexport async function saveGoogleTokens(incoming: StoredGoogleTokens, home?: string): Promise<StoredGoogleTokens> {\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","// Client for the mulmoserver OAuth broker (receptron/mulmoserver#54).\n//\n// Why a broker: Google requires a client_secret at the token endpoint even for\n// PKCE flows, so a user without their own Cloud project cannot complete a link\n// on their own. The broker holds the secret and applies it — it never stores or\n// returns it, and tokens still live only on this machine.\n//\n// What proves this host started the flow is the PKCE code_verifier: anyone can\n// mint a `state` at /googleOAuthStart, so the verifier — not the state — is the\n// authorization. See the broker's pkce.ts for the same reasoning server-side.\nimport type { Credentials } from \"google-auth-library\";\nimport { fetchWithTimeout } from \"./fetch.js\";\nimport { errorMessage, isRecord, ONE_SECOND_MS } from \"./util.js\";\n\nconst DEFAULT_BROKER_BASE_URL = \"https://asia-northeast1-mulmoserver.cloudfunctions.net\";\nconst BROKER_TIMEOUT_MS = 20 * ONE_SECOND_MS;\n\n// Trailing slashes are stripped by hand: `/\\/+$/` backtracks super-linearly on\n// a long run of slashes, which lint rejects.\nconst withoutTrailingSlashes = (url: string): string => {\n  let end = url.length;\n  while (end > 0 && url[end - 1] === \"/\") end -= 1;\n  return url.slice(0, end);\n};\n\n/** `MULMO_GOOGLE_BROKER_URL` lets a fork / staging deploy point elsewhere\n *  without a code change; unset means the shipped broker. */\nexport const brokerBaseUrl = (override: string | undefined = process.env.MULMO_GOOGLE_BROKER_URL): string =>\n  withoutTrailingSlashes(override ?? DEFAULT_BROKER_BASE_URL);\n\nexport interface BrokerStartResponse {\n  authUrl: string;\n  state: string;\n}\n\nconst LOOPBACK_HOSTNAMES = new Set([\"127.0.0.1\", \"localhost\", \"::1\", \"[::1]\"]);\n\n/** Codes, PKCE verifiers and refresh tokens travel to the broker, so cleartext\n *  is refused outright — an override pointed at `http://…` would put them on\n *  the wire. Loopback stays allowed: that is where tests stub the broker, and\n *  it never leaves the machine. */\nconst assertSecureBrokerUrl = (url: string): void => {\n  let parsed: URL;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`invalid Google sign-in service URL: ${url}`);\n  }\n  if (parsed.protocol === \"https:\") return;\n  if (parsed.protocol === \"http:\" && LOOPBACK_HOSTNAMES.has(parsed.hostname)) return;\n  throw new Error(`the Google sign-in service must be reached over HTTPS (got ${parsed.protocol}//${parsed.host})`);\n};\n\nconst brokerFetch = async (url: string, init: { method?: string; body?: string } = {}): Promise<unknown> => {\n  assertSecureBrokerUrl(url);\n  let response: Response;\n  try {\n    response = await fetchWithTimeout(url, {\n      ...init,\n      timeoutMs: BROKER_TIMEOUT_MS,\n      headers: { \"Content-Type\": \"application/json\" },\n    });\n  } catch (err) {\n    throw new Error(`Google sign-in service unreachable — check the network connection and retry (${errorMessage(err)})`);\n  }\n  if (!response.ok) {\n    // The broker answers deliberately opaque errors (it must not help probe\n    // codes / refresh tokens), so there is nothing more specific to surface.\n    throw new Error(`Google sign-in service returned HTTP ${response.status}`);\n  }\n  try {\n    return await response.json();\n  } catch {\n    // A proxy / captive portal answering 200 with HTML would otherwise surface\n    // as a raw SyntaxError the agent can't act on.\n    throw new Error(\"Google sign-in service returned a malformed response\");\n  }\n};\n\nconst GOOGLE_CONSENT_HOST = \"accounts.google.com\";\n\n/** The returned URL is opened in the user's browser, so it must be Google's\n *  own consent page and nothing else — a mis-pointed or hostile broker (the\n *  base URL is overridable) could otherwise hand back a phishing page wearing\n *  a sign-in flow. Cheap to assert, and Google's endpoint host is fixed. */\nconst assertGoogleConsentUrl = (authUrl: string): void => {\n  let parsed: URL;\n  try {\n    parsed = new URL(authUrl);\n  } catch {\n    throw new Error(\"Google sign-in service returned an unusable authorization URL\");\n  }\n  if (parsed.protocol !== \"https:\" || parsed.hostname !== GOOGLE_CONSENT_HOST) {\n    throw new Error(`Google sign-in service returned an authorization URL that is not Google's consent page (${parsed.protocol}//${parsed.host})`);\n  }\n};\n\nexport async function brokerStart(port: number, codeChallenge: string, baseUrl = brokerBaseUrl()): Promise<BrokerStartResponse> {\n  const params = new URLSearchParams({ port: String(port), code_challenge: codeChallenge });\n  const payload = await brokerFetch(`${baseUrl}/googleOAuthStart?${params.toString()}`);\n  const record = isRecord(payload) ? payload : {};\n  if (typeof record.auth_url !== \"string\" || typeof record.state !== \"string\") {\n    throw new Error(\"Google sign-in service returned an unexpected response (missing auth_url / state)\");\n  }\n  assertGoogleConsentUrl(record.auth_url);\n  return { authUrl: record.auth_url, state: record.state };\n}\n\nconst toCredentials = (payload: unknown, existingRefreshToken?: string): Credentials => {\n  const record = isRecord(payload) ? payload : {};\n  if (typeof record.access_token !== \"string\") {\n    throw new Error(\"Google sign-in service returned no access token\");\n  }\n  // The refresh endpoint does not echo the refresh_token back — keep the one we\n  // already hold so a renewal can't silently unlink the account.\n  const refreshToken = typeof record.refresh_token === \"string\" ? record.refresh_token : existingRefreshToken;\n  return {\n    access_token: record.access_token,\n    ...(refreshToken ? { refresh_token: refreshToken } : {}),\n    ...(typeof record.expiry_date === \"number\" ? { expiry_date: record.expiry_date } : {}),\n  };\n};\n\nexport async function brokerExchange(input: { code: string; state: string; codeVerifier: string }, baseUrl = brokerBaseUrl()): Promise<Credentials> {\n  const payload = await brokerFetch(`${baseUrl}/googleOAuthExchange`, {\n    method: \"POST\",\n    body: JSON.stringify({ code: input.code, state: input.state, code_verifier: input.codeVerifier }),\n  });\n  const credentials = toCredentials(payload);\n  if (!credentials.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 credentials;\n}\n\nexport async function brokerRefresh(refreshToken: string, baseUrl = brokerBaseUrl()): Promise<Credentials> {\n  const payload = await brokerFetch(`${baseUrl}/googleOAuthRefresh`, {\n    method: \"POST\",\n    body: JSON.stringify({ refresh_token: refreshToken }),\n  });\n  return toCredentials(payload, refreshToken);\n}\n","// Google OAuth for the host machine, independent of Firebase Auth (which\n// discards refresh tokens). Two ways to reach a linked account:\n//\n//   - LOCAL: the user dropped their own desktop-app client JSON in\n//     `~/.secrets/`. Everything (consent, exchange, refresh) happens on this\n//     machine — nothing but Google is contacted.\n//   - BROKER (the default): no client JSON, so the mulmoserver broker applies\n//     the client secret for the exchange / refresh it cannot be done without.\n//     Tokens still only ever live here. See broker.ts.\n//\n// Either way the loopback listener, the PKCE verifier, and the token file are\n// this machine's. `issuedVia` on the stored token records which path minted it\n// so renewals take the matching one.\nimport { randomBytes } from \"node:crypto\";\nimport http from \"node:http\";\nimport { CodeChallengeMethod, OAuth2Client, type Credentials } from \"google-auth-library\";\nimport { brokerExchange, brokerRefresh, brokerStart } from \"./broker.js\";\nimport { log } from \"./host.js\";\nimport { errorMessage, ONE_MINUTE_MS, ONE_SECOND_MS } from \"./util.js\";\nimport { fetchWithTimeout } from \"./fetch.js\";\nimport { clientSecretPresence, loadClientSecret, type InstalledClientSecret } from \"./clientSecret.js\";\nimport { deleteGoogleTokens, loadGoogleTokens, saveGoogleTokens, type IssuedVia } 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/** Renew a minute early so a call can't start with a token that expires\n *  mid-flight. */\nconst EXPIRY_MARGIN_MS = ONE_MINUTE_MS;\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\nconst REVOKED_GRANT_MESSAGE = \"could not obtain a Google access token — the grant may have been revoked; re-link the account\";\n\nconst localAccessToken = async (saved: Credentials, home?: string): Promise<string> => {\n  const presence = await clientSecretPresence(home);\n  // Two desktop clients: the token is still renewable — the engine just can't\n  // tell which client minted it. The fix is to remove the duplicate, so raise\n  // the loader's own wording rather than sending the user through a re-link.\n  if (presence === \"ambiguous\") await loadClientSecret(home);\n  // No client at all: the token is bound to the client_id that minted it, so\n  // nothing — not even the broker — can renew it. Re-linking is the only way\n  // out; the loader's \"no desktop client\" wording would read like a setup\n  // problem instead.\n  if (presence === \"missing\") {\n    throw new Error(\n      \"the saved Google link was created with an OAuth client that is no longer configured on this host — ask the user to link their Google account again in this app's settings\",\n    );\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) throw new Error(REVOKED_GRANT_MESSAGE);\n  return token;\n};\n\n// The broker mints access tokens because only it holds the client secret. The\n// refreshed token is written back so the next call can reuse it until expiry\n// instead of hitting the broker every time.\nconst brokerAccessToken = async (saved: Credentials, home?: string): Promise<string> => {\n  if (typeof saved.expiry_date === \"number\" && saved.access_token && saved.expiry_date - EXPIRY_MARGIN_MS > Date.now()) {\n    return saved.access_token;\n  }\n  const refreshed = await brokerRefresh(saved.refresh_token ?? \"\");\n  if (!refreshed.access_token) throw new Error(REVOKED_GRANT_MESSAGE);\n  await saveGoogleTokens(refreshed, home);\n  return refreshed.access_token;\n};\n\nexport async function getGoogleAccessToken(home?: string): Promise<string> {\n  const saved = await loadGoogleTokens(home);\n  if (!saved?.refresh_token) {\n    // Host-neutral wording — this engine ships to multiple hosts whose link\n    // flows differ (#2128); each host's own help carries the specific steps.\n    throw new Error(\"Google account not linked on this host — ask the user to link their Google account in this app's settings, then retry\");\n  }\n  // Tokens written before the broker existed carry no marker; they were all\n  // minted from a local client, so that stays the default.\n  return saved.issuedVia === \"broker\" ? await brokerAccessToken(saved, home) : await localAccessToken(saved, home);\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\n// PKCE material is generated by an OAuth2Client with no credentials — the\n// helper is pure crypto, and in broker mode there is no client to construct.\nconst generatePkce = async (): Promise<{ codeVerifier: string; codeChallenge: string }> => {\n  const { codeVerifier, codeChallenge } = await new OAuth2Client().generateCodeVerifierAsync();\n  if (!codeChallenge) throw new Error(\"failed to derive a PKCE code challenge\");\n  return { codeVerifier, codeChallenge };\n};\n\nconst authorizeWithLocalClient = async (\n  secret: InstalledClientSecret,\n  server: http.Server,\n  port: number,\n  opts: AuthorizeGoogleOptions,\n): Promise<Credentials> => {\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 tokens;\n};\n\n// The broker signs `state` with a key it never releases, so it — not this\n// host — builds the authorization URL. `state` then round-trips through the\n// browser and comes back to our loopback, where waitForAuthCode matches it.\nconst authorizeWithBroker = async (server: http.Server, port: number, opts: AuthorizeGoogleOptions): Promise<Credentials> => {\n  const { codeVerifier, codeChallenge } = await generatePkce();\n  const { authUrl, state } = await brokerStart(port, codeChallenge);\n  opts.onAuthUrl?.(authUrl);\n  const code = await waitForAuthCode(server, state, opts.timeoutMs ?? AUTH_TIMEOUT_MS);\n  return await brokerExchange({ code, state, codeVerifier });\n};\n\nexport async function authorizeGoogle(opts: AuthorizeGoogleOptions = {}): Promise<Credentials> {\n  // A user-supplied client wins: it keeps the whole flow on this machine, and\n  // silently preferring the broker would ignore a deliberate setup. Two of\n  // them is unresolvable rather than a reason to fall back — the user meant to\n  // use one of theirs, and a broker link would quietly not be it.\n  const presence = await clientSecretPresence(opts.home);\n  if (presence === \"ambiguous\") {\n    // Same wording the loader raises, so the CLI and the settings UI (which\n    // disables linking in this state) agree on the fix.\n    await loadClientSecret(opts.home);\n  }\n  const useLocalClient = presence === \"found\";\n  const { server, port } = await startLoopbackServer();\n  try {\n    const issuedVia: IssuedVia = useLocalClient ? \"local\" : \"broker\";\n    const tokens = useLocalClient\n      ? await authorizeWithLocalClient(await loadClientSecret(opts.home), server, port, opts)\n      : await authorizeWithBroker(server, port, opts);\n    return await saveGoogleTokens({ ...tokens, issuedVia }, 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","// Shared REST plumbing for the Google APIs this engine wraps (Calendar,\n// Tasks, Drive). Plain fetch instead of the `googleapis` SDK — a handful of\n// endpoints don't justify the dependency (see\n// plans/done/feat-google-oauth-calendar.md).\nimport { errorMessage, isRecord, ONE_SECOND_MS, truncate } from \"./util.js\";\nimport { fetchWithTimeout } from \"./fetch.js\";\n\nexport const GOOGLE_API_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\n/** 403 usually means the API is not enabled for the user's Cloud project —\n *  name the API so the agent's recovery guidance can be specific. */\nexport const googleApiError = (apiLabel: string, status: number, body: string): Error => {\n  const hint = status === HTTP_FORBIDDEN ? ` (is the ${apiLabel} enabled for the Cloud project?)` : \"\";\n  const detail = body ? ` — ${truncate(body, ERROR_BODY_MAX_CHARS)}` : \"\";\n  return new Error(`${apiLabel}: HTTP ${status}${hint}${detail}`);\n};\n\nexport interface GoogleRequestInit {\n  method?: string;\n  body?: string;\n  /** Overrides the default JSON content type (multipart upload, …). */\n  contentType?: string;\n  /** Response is not JSON (Drive media download) — return the raw text. */\n  expectText?: boolean;\n}\n\nexport async function googleRequest(apiLabel: string, accessToken: string, url: string, init: GoogleRequestInit = {}): Promise<unknown> {\n  const { contentType = \"application/json\", expectText = false, ...rest } = init;\n  const response = await fetchWithTimeout(url, {\n    ...rest,\n    timeoutMs: GOOGLE_API_TIMEOUT_MS,\n    headers: { Authorization: `Bearer ${accessToken}`, \"Content-Type\": contentType },\n  });\n  if (!response.ok) {\n    const body = await response.text().catch((err: unknown) => errorMessage(err));\n    throw googleApiError(apiLabel, response.status, body);\n  }\n  if (expectText) return await response.text();\n  // 204 (Drive delete, …) has no body to parse.\n  if (response.status === 204) return {};\n  return await response.json();\n}\n\nexport const stringField = (record: Record<string, unknown>, key: string): string => (typeof record[key] === \"string\" ? record[key] : \"\");\n\nexport const asRecord = (value: unknown): Record<string, unknown> => (isRecord(value) ? value : {});\n\nexport const itemsOf = (value: unknown): unknown[] => {\n  const record = asRecord(value);\n  return Array.isArray(record.items) ? record.items : [];\n};\n","// Google Calendar v3 REST calls against the user's primary calendar.\nimport { asRecord, googleApiError, googleRequest, stringField, DEFAULT_LIST_MAX_RESULTS } from \"./apiClient.js\";\nimport { isRecord } from \"./util.js\";\n\nconst CALENDAR_EVENTS_URL = \"https://www.googleapis.com/calendar/v3/calendars/primary/events\";\nconst CALENDAR_API_LABEL = \"Google Calendar API\";\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 = asRecord(value);\n  return {\n    id: stringField(record, \"id\"),\n    summary: stringField(record, \"summary\"),\n    start: eventTime(record.start),\n    end: eventTime(record.end),\n    htmlLink: stringField(record, \"htmlLink\"),\n    status: stringField(record, \"status\"),\n  };\n};\n\n/** Kept as a named export for the existing unit tests / callers; the shared\n *  helper now carries the wording. */\nexport const calendarApiError = (status: number, body: string): Error => googleApiError(CALENDAR_API_LABEL, status, body);\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 googleRequest(CALENDAR_API_LABEL, 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 googleRequest(CALENDAR_API_LABEL, accessToken, `${CALENDAR_EVENTS_URL}?${params.toString()}`);\n  const record = asRecord(listed);\n  const items = Array.isArray(record.items) ? record.items : [];\n  return items.map(toEventSummary);\n}\n","// Google Tasks v1 REST calls. `@default` is Google's alias for the user's\n// default task list, so callers can stay list-agnostic.\nimport { asRecord, googleRequest, itemsOf, stringField, DEFAULT_LIST_MAX_RESULTS } from \"./apiClient.js\";\n\nconst TASKS_BASE_URL = \"https://tasks.googleapis.com/tasks/v1\";\nconst TASKS_API_LABEL = \"Google Tasks API\";\nconst DEFAULT_TASK_LIST_ID = \"@default\";\nconst TASK_STATUS_COMPLETED = \"completed\";\nconst MAX_TASK_LISTS = 50;\n\nexport interface TaskListSummary {\n  id: string;\n  title: string;\n}\n\nexport interface TaskSummary {\n  id: string;\n  title: string;\n  status: string;\n  due: string;\n  notes: string;\n}\n\nexport interface ListTasksInput {\n  taskListId?: string;\n  maxResults?: number;\n  showCompleted?: boolean;\n}\n\nexport interface CreateTaskInput {\n  title: string;\n  notes?: string;\n  /** RFC3339. Google stores a DATE only — the time part is recorded but\n   *  ignored by the UI, so callers should not promise time-of-day fidelity. */\n  due?: string;\n  taskListId?: string;\n}\n\nexport interface CompleteTaskInput {\n  taskId: string;\n  taskListId?: string;\n}\n\nexport const toTaskListSummary = (value: unknown): TaskListSummary => {\n  const record = asRecord(value);\n  return { id: stringField(record, \"id\"), title: stringField(record, \"title\") };\n};\n\nexport const toTaskSummary = (value: unknown): TaskSummary => {\n  const record = asRecord(value);\n  return {\n    id: stringField(record, \"id\"),\n    title: stringField(record, \"title\"),\n    status: stringField(record, \"status\"),\n    due: stringField(record, \"due\"),\n    notes: stringField(record, \"notes\"),\n  };\n};\n\nconst tasksUrl = (taskListId: string | undefined, suffix = \"\"): string =>\n  `${TASKS_BASE_URL}/lists/${encodeURIComponent(taskListId ?? DEFAULT_TASK_LIST_ID)}/tasks${suffix}`;\n\nexport async function listTaskLists(accessToken: string): Promise<TaskListSummary[]> {\n  const listed = await googleRequest(TASKS_API_LABEL, accessToken, `${TASKS_BASE_URL}/users/@me/lists?maxResults=${MAX_TASK_LISTS}`);\n  return itemsOf(listed).map(toTaskListSummary);\n}\n\nexport async function listTasks(accessToken: string, input: ListTasksInput = {}): Promise<TaskSummary[]> {\n  const params = new URLSearchParams({\n    maxResults: String(input.maxResults ?? DEFAULT_LIST_MAX_RESULTS),\n    showCompleted: String(input.showCompleted ?? false),\n  });\n  const listed = await googleRequest(TASKS_API_LABEL, accessToken, `${tasksUrl(input.taskListId)}?${params.toString()}`);\n  return itemsOf(listed).map(toTaskSummary);\n}\n\nexport async function createTask(accessToken: string, input: CreateTaskInput): Promise<TaskSummary> {\n  const body = { title: input.title, notes: input.notes, due: input.due };\n  const created = await googleRequest(TASKS_API_LABEL, accessToken, tasksUrl(input.taskListId), { method: \"POST\", body: JSON.stringify(body) });\n  return toTaskSummary(created);\n}\n\nexport async function completeTask(accessToken: string, input: CompleteTaskInput): Promise<TaskSummary> {\n  // PATCH keeps the rest of the task intact — a PUT would need the full body\n  // and would silently drop fields the caller never read.\n  const url = tasksUrl(input.taskListId, `/${encodeURIComponent(input.taskId)}`);\n  const updated = await googleRequest(TASKS_API_LABEL, accessToken, url, {\n    method: \"PATCH\",\n    body: JSON.stringify({ status: TASK_STATUS_COMPLETED }),\n  });\n  return toTaskSummary(updated);\n}\n\nexport async function deleteTask(accessToken: string, input: CompleteTaskInput): Promise<void> {\n  const url = tasksUrl(input.taskListId, `/${encodeURIComponent(input.taskId)}`);\n  await googleRequest(TASKS_API_LABEL, accessToken, url, { method: \"DELETE\" });\n}\n","// Google Drive v3 REST calls under the `drive.file` scope — the app only\n// ever sees files IT created, never the user's wider Drive. That narrow\n// scope is why this is non-sensitive and needs no Google verification\n// review; keep every call inside it.\nimport { randomBytes } from \"node:crypto\";\nimport { asRecord, googleRequest, stringField, DEFAULT_LIST_MAX_RESULTS } from \"./apiClient.js\";\n\nconst DRIVE_FILES_URL = \"https://www.googleapis.com/drive/v3/files\";\nconst DRIVE_UPLOAD_URL = \"https://www.googleapis.com/upload/drive/v3/files\";\nconst DRIVE_API_LABEL = \"Google Drive API\";\nconst DEFAULT_MIME_TYPE = \"text/plain\";\nconst FILE_FIELDS = \"id,name,mimeType,webViewLink,modifiedTime\";\n// Reading a huge blob into a tool result would blow the context window; the\n// tool surface is for app-created text documents, not media.\nconst MAX_READ_CHARS = 100_000;\nconst TEXT_MIME_PREFIXES = [\"text/\", \"application/json\", \"application/xml\", \"application/javascript\"];\n\nexport interface DriveFileSummary {\n  id: string;\n  name: string;\n  mimeType: string;\n  webViewLink: string;\n  modifiedTime: string;\n}\n\nexport interface ListDriveFilesInput {\n  maxResults?: number;\n}\n\nexport interface CreateDriveFileInput {\n  name: string;\n  content: string;\n  mimeType?: string;\n}\n\nexport interface ReadDriveFileInput {\n  fileId: string;\n}\n\nexport const toDriveFileSummary = (value: unknown): DriveFileSummary => {\n  const record = asRecord(value);\n  return {\n    id: stringField(record, \"id\"),\n    name: stringField(record, \"name\"),\n    mimeType: stringField(record, \"mimeType\"),\n    webViewLink: stringField(record, \"webViewLink\"),\n    modifiedTime: stringField(record, \"modifiedTime\"),\n  };\n};\n\nexport const isTextMimeType = (mimeType: string): boolean => TEXT_MIME_PREFIXES.some((prefix) => mimeType.startsWith(prefix));\n\nexport async function listDriveFiles(accessToken: string, input: ListDriveFilesInput = {}): Promise<DriveFileSummary[]> {\n  const params = new URLSearchParams({\n    pageSize: String(input.maxResults ?? DEFAULT_LIST_MAX_RESULTS),\n    fields: `files(${FILE_FIELDS})`,\n    orderBy: \"modifiedTime desc\",\n  });\n  const listed = await googleRequest(DRIVE_API_LABEL, accessToken, `${DRIVE_FILES_URL}?${params.toString()}`);\n  const record = asRecord(listed);\n  const files = Array.isArray(record.files) ? record.files : [];\n  return files.map(toDriveFileSummary);\n}\n\n// Multipart upload: metadata part + media part in one request. Built by hand\n// because the `googleapis` SDK is the only alternative and this is the sole\n// multipart call in the engine.\nconst BOUNDARY_BYTES = 16;\n\n/** Fresh per request: a fixed boundary appearing inside file content would\n *  split the payload at the wrong place, so Drive would store a truncated or\n *  mangled file. Random 128 bits makes an accidental — or crafted — collision\n *  infeasible; `newMultipartBoundary` is re-derived until it is absent from\n *  the body it must delimit. */\nconst newMultipartBoundary = (): string => `mulmo-drive-${randomBytes(BOUNDARY_BYTES).toString(\"hex\")}`;\n\n// RFC 2045 token + parameters: no CR/LF (header injection into the part\n// headers) and no quotes/semicolons that would let a crafted value forge\n// extra headers or parameters.\nconst MIME_TYPE_RE = /^[A-Za-z0-9!#$&^_.+-]+\\/[A-Za-z0-9!#$&^_.+-]+$/;\n\nexport const assertSafeMimeType = (mimeType: string): string => {\n  if (!MIME_TYPE_RE.test(mimeType)) throw new Error(`Google Drive API: invalid mimeType '${mimeType}' — expected a plain type/subtype such as text/plain`);\n  return mimeType;\n};\n\nexport const buildMultipartBody = (metadata: Record<string, string>, content: string, mimeType: string, boundary: string): string =>\n  [\n    `--${boundary}`,\n    \"Content-Type: application/json; charset=UTF-8\",\n    \"\",\n    JSON.stringify(metadata),\n    `--${boundary}`,\n    `Content-Type: ${mimeType}`,\n    \"\",\n    content,\n    `--${boundary}--`,\n    \"\",\n  ].join(\"\\r\\n\");\n\n/** A boundary that appears nowhere in the parts it delimits. */\nexport const pickBoundary = (parts: string[], generate: () => string = newMultipartBoundary): string => {\n  const collides = (candidate: string): boolean => parts.some((part) => part.includes(candidate));\n  let boundary = generate();\n  while (collides(boundary)) boundary = generate();\n  return boundary;\n};\n\nexport async function createDriveFile(accessToken: string, input: CreateDriveFileInput): Promise<DriveFileSummary> {\n  const mimeType = assertSafeMimeType(input.mimeType ?? DEFAULT_MIME_TYPE);\n  const metadata = { name: input.name, mimeType };\n  const boundary = pickBoundary([input.content, JSON.stringify(metadata)]);\n  const params = new URLSearchParams({ uploadType: \"multipart\", fields: FILE_FIELDS });\n  const created = await googleRequest(DRIVE_API_LABEL, accessToken, `${DRIVE_UPLOAD_URL}?${params.toString()}`, {\n    method: \"POST\",\n    contentType: `multipart/related; boundary=${boundary}`,\n    body: buildMultipartBody(metadata, input.content, mimeType, boundary),\n  });\n  return toDriveFileSummary(created);\n}\n\nexport async function readDriveFile(accessToken: string, input: ReadDriveFileInput): Promise<{ file: DriveFileSummary; content: string }> {\n  const fileId = encodeURIComponent(input.fileId);\n  const metadata = await googleRequest(DRIVE_API_LABEL, accessToken, `${DRIVE_FILES_URL}/${fileId}?fields=${FILE_FIELDS}`);\n  const file = toDriveFileSummary(metadata);\n  if (!isTextMimeType(file.mimeType)) {\n    throw new Error(`Google Drive API: '${file.name}' is ${file.mimeType || \"a binary file\"} — only text files can be read as content`);\n  }\n  const raw = await googleRequest(DRIVE_API_LABEL, accessToken, `${DRIVE_FILES_URL}/${fileId}?alt=media`, { expectText: true });\n  const content = typeof raw === \"string\" ? raw.slice(0, MAX_READ_CHARS) : \"\";\n  return { file, content };\n}\n\nexport async function deleteDriveFile(accessToken: string, input: ReadDriveFileInput): Promise<void> {\n  await googleRequest(DRIVE_API_LABEL, accessToken, `${DRIVE_FILES_URL}/${encodeURIComponent(input.fileId)}`, { method: \"DELETE\" });\n}\n"],"mappings":"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"}