gsclaw 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"app-DdGjQNXR.js","names":["GOOGLE_TOKEN_URL","pkg.version","LOOPBACK","json","html","json"],"sources":["../src/core/activity.ts","../src/core/crypto.ts","../src/core/log.ts","../src/core/config.ts","../src/core/errors.ts","../src/core/gsc/sites.ts","../src/core/gsc/auth.ts","../package.json","../src/core/version.ts","../src/core/pages.ts","../src/core/principal.ts","../src/core/ratelimit.ts","../src/core/session.ts","../src/core/oauth/clients.ts","../src/core/oauth/google.ts","../src/core/oauth/pages.ts","../src/core/oauth/server.ts","../src/core/update-check.ts","../src/core/dashboard-api.ts","../src/core/seal.ts","../src/core/dashboard-shell.ts","../src/core/format.ts","../src/core/prompts.ts","../src/core/mcp.ts","../src/core/cache.ts","../src/core/gsc/client.ts","../src/core/analysis/compare.ts","../src/core/dates.ts","../src/core/gsc/types.ts","../src/core/tools/common.ts","../src/core/tools/types.ts","../src/core/tools/compare.ts","../src/core/analysis/decay.ts","../src/core/tools/content-decay.ts","../src/core/tools/inspection.ts","../src/core/analysis/ctr-curve.ts","../src/core/analysis/opportunities.ts","../src/core/tools/opportunities.ts","../src/core/tools/page-report.ts","../src/core/tools/search-analytics.ts","../src/core/tools/sites.ts","../src/core/tools/sitemaps.ts","../src/core/tools/registry.ts","../src/core/runtime.ts","../src/core/app.ts"],"sourcesContent":["export interface ActivityEntry {\n time: string;\n /** Best-effort client name (from the User-Agent or transport). */\n client: string;\n tool: string;\n site: string | null;\n ms: number;\n ok: boolean;\n error_kind?: string;\n /** Signed-in Google user (OAuth mode); used to show each person only their own calls. */\n user?: string;\n}\n\n/**\n * Recent tool calls for the dashboard's trust view: metadata only, never arguments beyond the\n * property or response payloads. In-memory and per instance: serverless platforms may run several\n * instances, and each keeps its own list.\n */\nexport class ActivityLog {\n readonly persistent = false;\n private readonly entries: ActivityEntry[] = [];\n\n constructor(private readonly capacity = 200) {}\n\n record(entry: ActivityEntry): void {\n this.entries.push(entry);\n if (this.entries.length > this.capacity) this.entries.shift();\n }\n\n list(limit = this.capacity): ActivityEntry[] {\n return this.entries.slice(-limit).reverse();\n }\n}\n\nconst KNOWN_CLIENTS: [RegExp, string][] = [\n [/claude-code|claude code/i, 'Claude Code'],\n [/claude/i, 'Claude'],\n [/chatgpt|openai/i, 'ChatGPT'],\n [/cursor/i, 'Cursor'],\n [/visual studio code|vscode|copilot/i, 'VS Code'],\n [/inspector/i, 'MCP Inspector'],\n [/openclaw/i, 'OpenClaw'],\n [/gemini/i, 'Gemini CLI'],\n [/codex/i, 'Codex'],\n];\n\n/** Maps a User-Agent to a short client label. */\nexport function clientLabel(userAgent: string | null): string {\n if (!userAgent) return 'unknown';\n for (const [pattern, label] of KNOWN_CLIENTS) if (pattern.test(userAgent)) return label;\n return userAgent.split(/[\\s/]/)[0]!.slice(0, 40) || 'unknown';\n}\n","// Runtime-agnostic crypto helpers built on WebCrypto, atob/btoa and TextEncoder only.\n\nconst encoder = new TextEncoder();\n\nexport function utf8(input: string): Uint8Array<ArrayBuffer> {\n return encoder.encode(input);\n}\n\nexport function bytesToBase64(bytes: Uint8Array): string {\n let binary = '';\n for (let i = 0; i < bytes.length; i += 0x8000) {\n binary += String.fromCharCode(...bytes.subarray(i, i + 0x8000));\n }\n return btoa(binary);\n}\n\nexport function base64ToBytes(base64: string): Uint8Array<ArrayBuffer> {\n const normalized = base64.replace(/-/g, '+').replace(/_/g, '/').replace(/\\s+/g, '');\n const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\nexport function base64UrlEncode(input: Uint8Array | string): string {\n const bytes = typeof input === 'string' ? utf8(input) : input;\n return bytesToBase64(bytes).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nexport async function sha256(input: string): Promise<Uint8Array> {\n return new Uint8Array(await crypto.subtle.digest('SHA-256', utf8(input)));\n}\n\nexport async function sha256Hex(input: string): Promise<string> {\n return [...(await sha256(input))].map((b) => b.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Constant-time string comparison. Both inputs are hashed first so the comparison time does not\n * depend on where the strings differ or on their lengths.\n */\nexport async function timingSafeEqual(a: string, b: string): Promise<boolean> {\n const [ha, hb] = await Promise.all([sha256(a), sha256(b)]);\n let diff = 0;\n for (let i = 0; i < ha.length; i++) diff |= ha[i]! ^ hb[i]!;\n return diff === 0;\n}\n\nexport function randomToken(bytes = 32): string {\n const buf = crypto.getRandomValues(new Uint8Array(bytes));\n return [...buf].map((b) => b.toString(16).padStart(2, '0')).join('');\n}\n","export const LOG_LEVELS = ['debug', 'info', 'warn', 'error', 'silent'] as const;\nexport type LogLevel = (typeof LOG_LEVELS)[number];\n\ntype Fields = Record<string, unknown>;\n\nexport interface Logger {\n debug(msg: string, fields?: Fields): void;\n info(msg: string, fields?: Fields): void;\n warn(msg: string, fields?: Fields): void;\n error(msg: string, fields?: Fields): void;\n child(fields: Fields): Logger;\n}\n\nexport type LogWriter = (line: string, level: Exclude<LogLevel, 'silent'>) => void;\n\nconst RANK: Record<LogLevel, number> = { debug: 10, info: 20, warn: 30, error: 40, silent: 100 };\n\n// Anything whose key looks like a credential is replaced before it reaches the log line.\nconst SECRET_KEY =\n /token|secret|password|authorization|cookie|private_?key|assertion|credential|api_?key/i;\n\nexport const defaultWriter: LogWriter = (line, level) => {\n if (level === 'error' || level === 'warn') console.error(line);\n else console.log(line);\n};\n\n/** For stdio transports: stdout carries the MCP protocol, so every log line goes to stderr. */\nexport const stderrWriter: LogWriter = (line) => console.error(line);\n\nexport function redact(value: unknown, depth = 0): unknown {\n if (value instanceof Error) return { name: value.name, message: value.message };\n if (depth > 4 || value === null || typeof value !== 'object') return value;\n if (Array.isArray(value)) return value.slice(0, 20).map((v) => redact(v, depth + 1));\n const out: Fields = {};\n for (const [key, v] of Object.entries(value)) {\n out[key] = SECRET_KEY.test(key) ? '[redacted]' : redact(v, depth + 1);\n }\n return out;\n}\n\nexport function createLogger(\n options: { level?: LogLevel; write?: LogWriter; base?: Fields } = {},\n): Logger {\n const threshold = RANK[options.level ?? 'info'];\n const write = options.write ?? defaultWriter;\n const base = options.base ?? {};\n\n const emit = (level: Exclude<LogLevel, 'silent'>, msg: string, fields?: Fields) => {\n if (RANK[level] < threshold) return;\n const entry = { time: new Date().toISOString(), level, msg, ...base, ...(fields ?? {}) };\n write(JSON.stringify(redact(entry)), level);\n };\n\n return {\n debug: (msg, fields) => emit('debug', msg, fields),\n info: (msg, fields) => emit('info', msg, fields),\n warn: (msg, fields) => emit('warn', msg, fields),\n error: (msg, fields) => emit('error', msg, fields),\n child: (fields) => createLogger({ level: options.level, write, base: { ...base, ...fields } }),\n };\n}\n\nexport const silentLogger: Logger = createLogger({ level: 'silent' });\n","import { base64ToBytes } from './crypto.js';\nimport { LOG_LEVELS, type LogLevel } from './log.js';\n\nexport type Env = Record<string, string | undefined>;\nexport type Transport = 'http' | 'stdio';\nexport type AuthMode = 'service_account' | 'oauth';\n\nexport interface ServiceAccountKey {\n client_email: string;\n private_key: string;\n private_key_id?: string;\n project_id?: string;\n token_uri?: string;\n}\n\nexport interface GoogleOAuthConfig {\n clientId: string;\n clientSecret: string;\n /** Lower-cased emails allowed to sign in (empty = no email restriction). */\n allowedEmails: string[];\n /** Lower-cased email domains allowed to sign in (empty = no domain restriction). */\n allowedDomains: string[];\n}\n\nexport interface Config {\n authMode: AuthMode;\n serviceAccount?: ServiceAccountKey;\n /** Shared secret clients present in service-account mode (bearer header or secret path). */\n accessToken?: string;\n /** Google OAuth (multi-user) mode settings. */\n googleOAuth?: GoogleOAuthConfig;\n /**\n * Seals OAuth tokens, codes and dashboard sessions: GSCLAW_ENCRYPTION_KEY and, in service-account\n * mode, the access token. Rotating either revokes everything issued with it. Absent for stdio.\n */\n sealingSecret?: string;\n /** Extra hosts allowed as OAuth redirect targets (besides the built-in client list). */\n oauthRedirectHosts: string[];\n publicBaseUrl?: string;\n allowWrites: boolean;\n dashboard: boolean;\n secretPath: boolean;\n allowedOrigins: string[];\n logLevel: LogLevel;\n rateLimitPerMinute: number;\n cacheTtlSeconds: number;\n maxRows: number;\n batchInspectMax: number;\n}\n\nexport interface ConfigIssue {\n variable?: string;\n message: string;\n}\n\nexport type ConfigResult =\n { ok: true; config: Config; warnings: string[] } | { ok: false; errors: ConfigIssue[] };\n\nexport const MIN_ACCESS_TOKEN_LENGTH = 32;\nexport const SETUP_DOCS_URL = 'https://github.com/tuyakhov/gsclaw/blob/main/docs/setup.md';\n\n/**\n * Loads and validates configuration from environment variables. Never throws.\n *\n * Error messages name variables but never echo their values: on serverless deployments they are\n * shown on the public setup page, and a secret pasted into the wrong variable must not leak.\n */\nexport function loadConfig(env: Env, opts: { transport: Transport }): ConfigResult {\n const errors: ConfigIssue[] = [];\n const warnings: string[] = [];\n const read = (key: string) => {\n const value = env[key]?.trim();\n return value ? value : undefined;\n };\n\n const hasServiceAccount = Boolean(read('GOOGLE_SERVICE_ACCOUNT_JSON'));\n const hasOAuth = Boolean(read('GOOGLE_OAUTH_CLIENT_ID') || read('GOOGLE_OAUTH_CLIENT_SECRET'));\n const explicitMode = read('GSCLAW_AUTH_MODE');\n\n let authMode: AuthMode | undefined;\n if (explicitMode) {\n if (explicitMode === 'service_account' || explicitMode === 'oauth') authMode = explicitMode;\n else\n errors.push({\n variable: 'GSCLAW_AUTH_MODE',\n message: 'must be \"service_account\" or \"oauth\".',\n });\n } else if (hasServiceAccount && hasOAuth) {\n errors.push({\n variable: 'GSCLAW_AUTH_MODE',\n message:\n 'both service-account and Google OAuth variables are set. Set GSCLAW_AUTH_MODE to \"service_account\" or \"oauth\" to choose one.',\n });\n } else if (hasServiceAccount) authMode = 'service_account';\n else if (hasOAuth) authMode = 'oauth';\n else {\n errors.push({\n message:\n 'No authentication mode is configured, so GSClaw refuses to start (it never runs open). ' +\n (opts.transport === 'http'\n ? 'Set GOOGLE_SERVICE_ACCOUNT_JSON and GSCLAW_ACCESS_TOKEN (service-account mode), or GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET, GSCLAW_ENCRYPTION_KEY and PUBLIC_BASE_URL (Google OAuth mode).'\n : 'Set GOOGLE_SERVICE_ACCOUNT_JSON or GOOGLE_APPLICATION_CREDENTIALS.') +\n ` See ${SETUP_DOCS_URL}`,\n });\n }\n\n let serviceAccount: ServiceAccountKey | undefined;\n let accessToken: string | undefined;\n\n if (authMode === 'service_account') {\n const raw = read('GOOGLE_SERVICE_ACCOUNT_JSON');\n if (!raw) {\n errors.push({\n variable: 'GOOGLE_SERVICE_ACCOUNT_JSON',\n message: 'is required in service-account mode (paste the key JSON, raw or base64).',\n });\n } else {\n const parsed = parseServiceAccountJson(raw);\n if ('error' in parsed)\n errors.push({ variable: 'GOOGLE_SERVICE_ACCOUNT_JSON', message: parsed.error });\n else serviceAccount = parsed.key;\n }\n\n if (opts.transport === 'http') {\n accessToken = read('GSCLAW_ACCESS_TOKEN');\n if (!accessToken) {\n errors.push({\n variable: 'GSCLAW_ACCESS_TOKEN',\n message:\n 'is required for HTTP deployments so the endpoint is never public. Generate one with `openssl rand -hex 32` or `npx gsclaw generate-token`.',\n });\n } else if (accessToken.length < MIN_ACCESS_TOKEN_LENGTH) {\n errors.push({\n variable: 'GSCLAW_ACCESS_TOKEN',\n message: `must be at least ${MIN_ACCESS_TOKEN_LENGTH} characters. Generate one with \\`openssl rand -hex 32\\`.`,\n });\n }\n }\n }\n\n const encryptionKey = read('GSCLAW_ENCRYPTION_KEY');\n if (encryptionKey && encryptionKey.length < MIN_ACCESS_TOKEN_LENGTH) {\n errors.push({\n variable: 'GSCLAW_ENCRYPTION_KEY',\n message: `must be at least ${MIN_ACCESS_TOKEN_LENGTH} characters. Generate one with \\`openssl rand -hex 32\\`.`,\n });\n }\n\n let googleOAuth: GoogleOAuthConfig | undefined;\n if (authMode === 'oauth') {\n if (opts.transport === 'stdio') {\n errors.push({\n variable: 'GSCLAW_AUTH_MODE',\n message:\n 'Google OAuth mode is for HTTP deployments. For local stdio use, set GOOGLE_SERVICE_ACCOUNT_JSON or GOOGLE_APPLICATION_CREDENTIALS instead.',\n });\n }\n const clientId = read('GOOGLE_OAUTH_CLIENT_ID');\n const clientSecret = read('GOOGLE_OAUTH_CLIENT_SECRET');\n if (!clientId) {\n errors.push({\n variable: 'GOOGLE_OAUTH_CLIENT_ID',\n message:\n 'is required in OAuth mode (Google Cloud → APIs & Services → Credentials → OAuth client ID).',\n });\n }\n if (!clientSecret) {\n errors.push({\n variable: 'GOOGLE_OAUTH_CLIENT_SECRET',\n message: 'is required in OAuth mode.',\n });\n }\n if (!encryptionKey) {\n errors.push({\n variable: 'GSCLAW_ENCRYPTION_KEY',\n message:\n 'is required in OAuth mode; it encrypts the tokens GSClaw issues. Generate one with `openssl rand -hex 32`.',\n });\n }\n const list = (key: string) =>\n (read(key) ?? '')\n .split(',')\n .map((v) => v.trim().toLowerCase().replace(/^@/, ''))\n .filter(Boolean);\n if (clientId && clientSecret) {\n googleOAuth = {\n clientId,\n clientSecret,\n allowedEmails: list('ALLOWED_GOOGLE_EMAILS'),\n allowedDomains: list('ALLOWED_GOOGLE_DOMAINS'),\n };\n }\n }\n\n const bool = (key: string, fallback: boolean) => {\n const value = read(key);\n if (value === undefined) return fallback;\n const v = value.toLowerCase();\n if (['1', 'true', 'yes', 'on'].includes(v)) return true;\n if (['0', 'false', 'no', 'off'].includes(v)) return false;\n errors.push({ variable: key, message: 'must be \"true\" or \"false\".' });\n return fallback;\n };\n\n const int = (key: string, fallback: number, min: number, max: number) => {\n const value = read(key);\n if (value === undefined) return fallback;\n const n = Number(value);\n if (!Number.isInteger(n) || n < min || n > max) {\n errors.push({\n variable: key,\n message: `must be an integer between ${min} and ${max}.`,\n });\n return fallback;\n }\n return n;\n };\n\n const logLevelRaw = (read('LOG_LEVEL') ?? 'info').toLowerCase();\n let logLevel: LogLevel = 'info';\n if ((LOG_LEVELS as readonly string[]).includes(logLevelRaw)) logLevel = logLevelRaw as LogLevel;\n else errors.push({ variable: 'LOG_LEVEL', message: `must be one of ${LOG_LEVELS.join(', ')}.` });\n\n let publicBaseUrl = read('PUBLIC_BASE_URL') ?? detectPlatformBaseUrl(env);\n if (publicBaseUrl) {\n try {\n const url = new URL(publicBaseUrl);\n publicBaseUrl = url.origin + url.pathname.replace(/\\/+$/, '');\n } catch {\n errors.push({\n variable: 'PUBLIC_BASE_URL',\n message: 'is not a valid URL (expected e.g. https://gsclaw.example.com).',\n });\n publicBaseUrl = undefined;\n }\n }\n\n const allowedOrigins: string[] = [];\n for (const entry of (read('GSCLAW_ALLOWED_ORIGINS') ?? '').split(',')) {\n const value = entry.trim();\n if (!value) continue;\n try {\n allowedOrigins.push(new URL(value).origin);\n } catch {\n errors.push({\n variable: 'GSCLAW_ALLOWED_ORIGINS',\n message: 'contains an invalid origin (expected e.g. https://app.example.com).',\n });\n }\n }\n\n const allowWrites = bool('GSCLAW_ALLOW_WRITES', false);\n const dashboard = bool('GSCLAW_DASHBOARD', true);\n const secretPath = bool('GSCLAW_SECRET_PATH', true);\n const rateLimitPerMinute = int('GSCLAW_RATE_LIMIT_PER_MINUTE', 120, 0, 100_000);\n const cacheTtlSeconds = int('GSCLAW_CACHE_TTL_SECONDS', 300, 0, 86_400);\n const maxRows = int('GSCLAW_MAX_ROWS', 25_000, 1, 500_000);\n const batchInspectMax = int('GSCLAW_BATCH_INSPECT_MAX', 50, 1, 500);\n\n const oauthRedirectHosts = (read('GSCLAW_OAUTH_REDIRECT_HOSTS') ?? '')\n .split(',')\n .map((h) => h.trim().toLowerCase())\n .filter(Boolean);\n\n if (authMode === 'oauth' && opts.transport === 'http') {\n if (!publicBaseUrl) {\n errors.push({\n variable: 'PUBLIC_BASE_URL',\n message:\n 'is required in OAuth mode (e.g. https://gsclaw.example.com); Google redirects back to PUBLIC_BASE_URL/oauth/google/callback.',\n });\n } else {\n const url = new URL(publicBaseUrl);\n const loopback = ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname);\n if (url.protocol !== 'https:' && !loopback) {\n errors.push({ variable: 'PUBLIC_BASE_URL', message: 'must use https in OAuth mode.' });\n }\n }\n }\n\n if (errors.length > 0 || !authMode) return { ok: false, errors };\n\n if (\n googleOAuth &&\n googleOAuth.allowedEmails.length === 0 &&\n googleOAuth.allowedDomains.length === 0\n ) {\n warnings.push(\n 'No ALLOWED_GOOGLE_EMAILS or ALLOWED_GOOGLE_DOMAINS set: anyone with a Google account can sign in to this deployment (each person only sees their own Search Console properties).',\n );\n }\n\n if (allowWrites) {\n warnings.push(\n 'GSCLAW_ALLOW_WRITES=true: submit_sitemap and delete_sitemap are enabled. The service account needs Full permission on each property.',\n );\n }\n\n return {\n ok: true,\n warnings,\n config: {\n authMode,\n serviceAccount,\n accessToken,\n googleOAuth,\n // In service-account mode the access token is always part of the secret, so rotating it\n // revokes every OAuth token and dashboard session issued with the old one.\n sealingSecret:\n opts.transport !== 'http'\n ? undefined\n : accessToken\n ? [encryptionKey, accessToken].filter(Boolean).join('\\n')\n : encryptionKey,\n oauthRedirectHosts,\n publicBaseUrl,\n allowWrites,\n dashboard,\n secretPath,\n allowedOrigins,\n logLevel,\n rateLimitPerMinute,\n cacheTtlSeconds,\n maxRows,\n batchInspectMax,\n },\n };\n}\n\nexport function parseServiceAccountJson(\n raw: string,\n): { key: ServiceAccountKey } | { error: string } {\n let text = raw.trim();\n if (!text.startsWith('{')) {\n try {\n text = new TextDecoder().decode(base64ToBytes(text)).trim();\n } catch {\n return { error: 'is neither JSON nor base64-encoded JSON.' };\n }\n }\n let data: unknown;\n try {\n data = JSON.parse(text);\n } catch {\n return {\n error:\n 'is not valid JSON. Paste the whole key file you downloaded from Google Cloud (or base64-encode it).',\n };\n }\n if (!data || typeof data !== 'object') return { error: 'must be a JSON object.' };\n const obj = data as Record<string, unknown>;\n if (obj.type !== undefined && obj.type !== 'service_account') {\n return {\n error: `has type \"${JSON.stringify(obj.type)}\" but must be a service-account key (\"type\": \"service_account\"). OAuth client JSON files belong in GOOGLE_OAUTH_CLIENT_ID/SECRET instead.`,\n };\n }\n if (typeof obj.client_email !== 'string' || !obj.client_email.includes('@')) {\n return { error: 'is missing \"client_email\".' };\n }\n if (typeof obj.private_key !== 'string' || !obj.private_key.includes('PRIVATE KEY')) {\n return { error: 'is missing \"private_key\".' };\n }\n // Env UIs often turn real newlines into literal \"\\n\" sequences.\n const privateKey = obj.private_key.includes('\\n')\n ? obj.private_key\n : obj.private_key.replace(/\\\\n/g, '\\n');\n\n return {\n key: {\n client_email: obj.client_email,\n private_key: privateKey,\n private_key_id: typeof obj.private_key_id === 'string' ? obj.private_key_id : undefined,\n project_id: typeof obj.project_id === 'string' ? obj.project_id : undefined,\n token_uri: typeof obj.token_uri === 'string' ? obj.token_uri : undefined,\n },\n };\n}\n\n/** Best-effort public URL detection so one-click deploys work without PUBLIC_BASE_URL. */\nexport function detectPlatformBaseUrl(env: Env): string | undefined {\n const https = (host?: string) =>\n host ? `https://${host.replace(/^https?:\\/\\//, '')}` : undefined;\n return (\n https(env.VERCEL_PROJECT_PRODUCTION_URL) ??\n env.URL ?? // Netlify\n env.RENDER_EXTERNAL_URL ??\n https(env.RAILWAY_PUBLIC_DOMAIN) ??\n env.APP_URL ?? // DigitalOcean App Platform\n (env.FLY_APP_NAME ? `https://${env.FLY_APP_NAME}.fly.dev` : undefined)\n );\n}\n\nexport function formatConfigErrors(errors: ConfigIssue[]): string {\n const lines = errors.map((e) => ` • ${e.variable ? `${e.variable} ` : ''}${e.message}`);\n return [\n 'GSClaw is not configured correctly and will not start:',\n '',\n ...lines,\n '',\n `Setup guide: ${SETUP_DOCS_URL}`,\n ].join('\\n');\n}\n","import type { GoogleErrorBody } from './gsc/types.js';\n\n/** An error whose message is written for the end user (and their AI) and safe to show verbatim. */\nexport class UserFacingError extends Error {\n readonly kind: string;\n constructor(message: string, kind = 'invalid_input') {\n super(message);\n this.name = 'UserFacingError';\n this.kind = kind;\n }\n}\n\nexport type GscErrorKind =\n | 'permission'\n | 'api_disabled'\n | 'not_found'\n | 'invalid_argument'\n | 'rate_limited'\n | 'quota_exceeded'\n | 'auth'\n | 'server'\n | 'network';\n\nexport interface Identity {\n kind: 'service_account' | 'user';\n email: string;\n projectId?: string;\n}\n\nexport class GscApiError extends UserFacingError {\n readonly status: number;\n readonly reason?: string;\n readonly googleMessage: string;\n readonly retryable: boolean;\n\n constructor(opts: {\n kind: GscErrorKind;\n status: number;\n advice: string;\n googleMessage: string;\n reason?: string;\n retryable?: boolean;\n }) {\n const suffix = opts.googleMessage ? `\\n\\nGoogle said: ${opts.googleMessage}` : '';\n super(`${opts.advice}${suffix}`, opts.kind);\n this.name = 'GscApiError';\n this.status = opts.status;\n this.reason = opts.reason;\n this.googleMessage = opts.googleMessage;\n this.retryable = opts.retryable ?? false;\n }\n}\n\nconst RATE_LIMIT_REASONS = new Set([\n 'rateLimitExceeded',\n 'userRateLimitExceeded',\n 'rateLimitExceededUnreg',\n]);\nconst DAILY_QUOTA_REASONS = new Set([\n 'quotaExceeded',\n 'dailyLimitExceeded',\n 'dailyLimitExceededUnreg',\n]);\n\nfunction who(identity: Identity): string {\n return identity.kind === 'service_account'\n ? `The service account ${identity.email}`\n : `Your Google account (${identity.email})`;\n}\n\n/** Turns a Google API error response into an actionable message. */\nexport function toGscApiError(\n status: number,\n body: unknown,\n ctx: { identity: Identity; siteUrl?: string },\n): GscApiError {\n const err = (body as GoogleErrorBody | undefined)?.error;\n const googleMessage = (err?.message ?? (typeof body === 'string' ? body : '')).slice(0, 500);\n const reasons = new Set<string>();\n for (const e of err?.errors ?? []) if (e.reason) reasons.add(e.reason);\n for (const d of err?.details ?? []) if (d.reason) reasons.add(d.reason);\n if (err?.status) reasons.add(err.status);\n const reason = [...reasons][0];\n const has = (...names: string[]) => names.some((n) => reasons.has(n));\n const site = ctx.siteUrl ? `'${ctx.siteUrl}'` : 'this property';\n const { identity } = ctx;\n\n if (\n status === 429 ||\n has(...RATE_LIMIT_REASONS) ||\n (has('RESOURCE_EXHAUSTED') && !has(...DAILY_QUOTA_REASONS))\n ) {\n return new GscApiError({\n kind: 'rate_limited',\n status,\n reason,\n googleMessage,\n retryable: true,\n advice:\n \"Google's Search Console API rate limit was hit (Search Analytics allows ~1,200 queries/min per site; URL Inspection 600/min). \" +\n 'Wait a minute and retry. If it keeps happening, use shorter date ranges or fewer page/query breakdowns, which count heavier against the load quota.',\n });\n }\n\n if (has(...DAILY_QUOTA_REASONS)) {\n return new GscApiError({\n kind: 'quota_exceeded',\n status,\n reason,\n googleMessage,\n advice:\n 'The daily Search Console API quota is used up (URL Inspection allows 2,000 inspections per property per day). It resets at midnight Pacific Time.',\n });\n }\n\n if (\n has('SERVICE_DISABLED', 'accessNotConfigured') ||\n /has not been used in project|is disabled/i.test(googleMessage)\n ) {\n const project = identity.projectId ?? googleMessage.match(/project (\\d+|[a-z][a-z0-9-]+)/)?.[1];\n const link = `https://console.cloud.google.com/apis/library/searchconsole.googleapis.com${project ? `?project=${project}` : ''}`;\n return new GscApiError({\n kind: 'api_disabled',\n status,\n reason,\n googleMessage,\n advice: `The Google Search Console API is not enabled for the Google Cloud project${project ? ` \"${project}\"` : ''}. Enable it at ${link}, wait a minute, then retry.`,\n });\n }\n\n if (status === 401) {\n return new GscApiError({\n kind: 'auth',\n status,\n reason,\n googleMessage,\n advice:\n identity.kind === 'service_account'\n ? 'Google rejected the service-account credentials. The key may have been deleted or rotated: create a new JSON key in Google Cloud and update GOOGLE_SERVICE_ACCOUNT_JSON.'\n : 'Your Google sign-in has expired or was revoked. Reconnect GSClaw in your AI client to sign in again.',\n });\n }\n\n if (status === 403) {\n const fix =\n identity.kind === 'service_account'\n ? `In Search Console, open the property → Settings → Users and permissions → Add user, and add ${identity.email} (Restricted is enough for reading; Full is needed for sitemap changes).`\n : 'Ask an owner of the property to add your Google account in Search Console → Settings → Users and permissions.';\n return new GscApiError({\n kind: 'permission',\n status,\n reason,\n googleMessage,\n advice:\n `${who(identity)} does not have access to ${site}. ${fix} ` +\n \"Also check the property format: domain properties look like 'sc-domain:example.com', URL-prefix properties like 'https://www.example.com/' (exact scheme, www and trailing slash). Run list_sites to see every property this server can read.\",\n });\n }\n\n if (status === 404) {\n return new GscApiError({\n kind: 'not_found',\n status,\n reason,\n googleMessage,\n advice: `Not found. Check the property and URL: run list_sites for valid properties, and pass sitemaps as full URLs exactly as list_sitemaps shows them.`,\n });\n }\n\n if (status === 400) {\n return new GscApiError({\n kind: 'invalid_argument',\n status,\n reason,\n googleMessage,\n advice:\n 'Google rejected the request parameters. Common causes: searchAppearance combined with other dimensions, aggregation_type \"byProperty\" with a page dimension/filter, Discover/Google News with query or position data, or dates older than 16 months.',\n });\n }\n\n return new GscApiError({\n kind: 'server',\n status,\n reason,\n googleMessage,\n retryable: status >= 500,\n advice: `Google's Search Console API returned an error (HTTP ${status}). This is usually temporary; try again shortly.`,\n });\n}\n\n/** Message for any thrown value, safe to return to clients. Unknown errors are not echoed in detail. */\nexport function publicErrorMessage(error: unknown): { message: string; kind: string } {\n if (error instanceof UserFacingError) return { message: error.message, kind: error.kind };\n return {\n message: 'Unexpected internal error. Check the server logs for details.',\n kind: 'internal',\n };\n}\n","import { UserFacingError } from '../errors.js';\nimport type { GscClient } from './client.js';\nimport type { SiteEntry } from './types.js';\n\nexport type PermissionLevel = 'owner' | 'full' | 'restricted' | 'unverified' | 'unknown';\n\nexport interface Property {\n site_url: string;\n type: 'domain' | 'url_prefix';\n /** Domain for `sc-domain:` properties, host for URL-prefix properties. */\n host: string;\n permission_level: PermissionLevel;\n /** False for unverified entries, which expose no data. */\n has_access: boolean;\n}\n\nexport function normalizePermission(level: string): PermissionLevel {\n switch (level.replace(/_/g, '').toLowerCase()) {\n case 'siteowner':\n return 'owner';\n case 'sitefulluser':\n return 'full';\n case 'siterestricteduser':\n return 'restricted';\n case 'siteunverifieduser':\n return 'unverified';\n default:\n return 'unknown';\n }\n}\n\nexport function toProperty(entry: SiteEntry): Property {\n const permission = normalizePermission(entry.permissionLevel);\n const isDomain = entry.siteUrl.startsWith('sc-domain:');\n let host = entry.siteUrl;\n if (isDomain) host = entry.siteUrl.slice('sc-domain:'.length);\n else {\n try {\n host = new URL(entry.siteUrl).host;\n } catch {\n // keep raw value\n }\n }\n return {\n site_url: entry.siteUrl,\n type: isDomain ? 'domain' : 'url_prefix',\n host: host.toLowerCase(),\n permission_level: permission,\n has_access: permission !== 'unverified',\n };\n}\n\nexport async function listProperties(\n client: GscClient,\n opts: { fresh?: boolean } = {},\n): Promise<Property[]> {\n const entries = await client.listSites(opts);\n return entries\n .map(toProperty)\n .sort(\n (a, b) =>\n Number(b.has_access) - Number(a.has_access) ||\n Number(b.type === 'domain') - Number(a.type === 'domain') ||\n a.site_url.localeCompare(b.site_url),\n );\n}\n\nfunction stripWww(host: string): string {\n return host.replace(/^www\\./, '');\n}\n\n/** True if `property` would contain data for `url`. */\nexport function propertyCoversUrl(property: Property, url: string): boolean {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return false;\n }\n const host = parsed.host.toLowerCase();\n if (property.type === 'domain') {\n return host === property.host || host.endsWith(`.${property.host}`);\n }\n return url.startsWith(property.site_url) || `${url}/` === property.site_url;\n}\n\nfunction describeAvailable(properties: Property[]): string {\n const usable = properties.filter((p) => p.has_access);\n if (usable.length === 0) {\n return 'This server cannot read any Search Console property yet. Add its Google identity as a user on your properties (Search Console → Settings → Users and permissions).';\n }\n const list = usable\n .slice(0, 20)\n .map((p) => `'${p.site_url}'`)\n .join(', ');\n return `Available properties: ${list}${usable.length > 20 ? `, … (${usable.length} total)` : ''}.`;\n}\n\n/**\n * Resolves user input ('sc-domain:example.com', 'https://www.example.com/', 'example.com',\n * or any URL on the site) to a property this identity can read.\n */\nexport async function resolveProperty(client: GscClient, input: string): Promise<Property> {\n const properties = await listProperties(client);\n const usable = properties.filter((p) => p.has_access);\n const raw = input.trim();\n const lower = raw.toLowerCase();\n\n const exact = properties.find(\n (p) => p.site_url.toLowerCase() === lower || p.site_url.toLowerCase() === `${lower}/`,\n );\n if (exact) {\n if (!exact.has_access) {\n throw new UserFacingError(\n `'${exact.site_url}' is listed but unverified for this identity, so Search Console exposes no data for it. Verify ownership or ask an owner for access. ${describeAvailable(properties)}`,\n 'permission',\n );\n }\n return exact;\n }\n\n if (lower.startsWith('sc-domain:')) {\n throw new UserFacingError(\n `No access to '${raw}'. ${describeAvailable(properties)}`,\n 'not_found',\n );\n }\n\n const hasScheme = /^https?:\\/\\//.test(lower);\n let url: URL;\n try {\n url = new URL(hasScheme ? raw : `https://${raw}`);\n } catch {\n throw new UserFacingError(\n `'${raw}' is not a valid property, domain or URL. ${describeAvailable(properties)}`,\n );\n }\n const host = url.host.toLowerCase();\n\n const domainMatch = usable\n .filter((p) => p.type === 'domain' && (host === p.host || host.endsWith(`.${p.host}`)))\n .sort((a, b) => b.host.length - a.host.length)[0];\n\n const prefixMatches = usable\n .filter((p) => p.type === 'url_prefix')\n .filter((p) => {\n if (hasScheme) return propertyCoversUrl(p, url.href);\n // Bare domains match any scheme and www/non-www variant of the same host.\n return (\n stripWww(p.host) === stripWww(host) &&\n (url.pathname === '/' || propertyCoversUrl(p, `https://${p.host}${url.pathname}`))\n );\n })\n .sort((a, b) => {\n if (b.site_url.length !== a.site_url.length && hasScheme)\n return b.site_url.length - a.site_url.length;\n // Prefer https and the exact host the user typed.\n const score = (p: Property) =>\n (p.site_url.startsWith('https://') ? 2 : 0) + (p.host === host ? 1 : 0);\n return score(b) - score(a);\n });\n\n // A full URL is most specific to its URL-prefix property; a bare domain is best served by the\n // domain property, which covers every host and protocol.\n const match = hasScheme ? (prefixMatches[0] ?? domainMatch) : (domainMatch ?? prefixMatches[0]);\n if (match) return match;\n\n throw new UserFacingError(\n `No Search Console property matches '${raw}'. ${describeAvailable(properties)}`,\n 'not_found',\n );\n}\n\n/** Resolves a page given as an absolute URL or a path ('/pricing') against a property. */\nexport function absolutePageUrl(property: Property, page: string): string | null {\n if (/^https?:\\/\\//i.test(page)) return page;\n if (!page.startsWith('/')) return null;\n if (property.type === 'url_prefix')\n return new URL(page.replace(/^\\//, ''), property.site_url).href;\n return null;\n}\n","import type { ServiceAccountKey } from '../config.js';\nimport { base64ToBytes, base64UrlEncode, utf8 } from '../crypto.js';\nimport { GscApiError, type Identity } from '../errors.js';\n\nexport const SCOPE_READONLY = 'https://www.googleapis.com/auth/webmasters.readonly';\nexport const SCOPE_READWRITE = 'https://www.googleapis.com/auth/webmasters';\nexport const GOOGLE_TOKEN_URL = 'https://oauth2.googleapis.com/token';\n\n/** Supplies Google access tokens for one identity (a service account or a signed-in user). */\nexport interface TokenSource {\n readonly identity: Identity;\n readonly scopes: string[];\n getAccessToken(): Promise<string>;\n /** Drops any cached token, e.g. after Google answered 401. */\n invalidate(): void;\n}\n\nconst EXPIRY_MARGIN_MS = 60_000;\n\n/** WebCrypto key type without depending on DOM or node:crypto typings. */\ntype SigningKey = Awaited<ReturnType<typeof crypto.subtle.importKey>>;\n\n/**\n * Service-account tokens via the OAuth 2.0 JWT bearer flow, signed with WebCrypto (RS256) so it\n * runs on Node and Cloudflare Workers alike. The imported key and the access token are cached for\n * the life of the instance.\n */\nexport class ServiceAccountTokenSource implements TokenSource {\n readonly identity: Identity;\n readonly scopes: string[];\n private readonly key: ServiceAccountKey;\n private readonly fetchImpl: typeof fetch;\n private readonly now: () => number;\n private cryptoKey?: Promise<SigningKey>;\n private cached?: { token: string; expiresAt: number };\n private inflight?: Promise<string>;\n\n constructor(\n key: ServiceAccountKey,\n scopes: string[],\n opts: { fetch?: typeof fetch; now?: () => number } = {},\n ) {\n this.key = key;\n this.scopes = scopes;\n this.fetchImpl = opts.fetch ?? ((input, init) => fetch(input, init));\n this.now = opts.now ?? Date.now;\n this.identity = { kind: 'service_account', email: key.client_email, projectId: key.project_id };\n }\n\n async getAccessToken(): Promise<string> {\n if (this.cached && this.cached.expiresAt - EXPIRY_MARGIN_MS > this.now())\n return this.cached.token;\n this.inflight ??= this.fetchToken().finally(() => {\n this.inflight = undefined;\n });\n return this.inflight;\n }\n\n invalidate(): void {\n this.cached = undefined;\n }\n\n /** Builds the signed JWT assertion. Exposed for tests. */\n async createAssertion(): Promise<string> {\n const iat = Math.floor(this.now() / 1000);\n const header = {\n alg: 'RS256',\n typ: 'JWT',\n ...(this.key.private_key_id ? { kid: this.key.private_key_id } : {}),\n };\n const claims = {\n iss: this.key.client_email,\n scope: this.scopes.join(' '),\n aud: this.key.token_uri ?? GOOGLE_TOKEN_URL,\n iat,\n exp: iat + 3600,\n };\n const signingInput = `${base64UrlEncode(JSON.stringify(header))}.${base64UrlEncode(JSON.stringify(claims))}`;\n this.cryptoKey ??= importPkcs8(this.key.private_key);\n const signature = await crypto.subtle.sign(\n 'RSASSA-PKCS1-v1_5',\n await this.cryptoKey,\n utf8(signingInput),\n );\n return `${signingInput}.${base64UrlEncode(new Uint8Array(signature))}`;\n }\n\n private async fetchToken(): Promise<string> {\n let assertion: string;\n try {\n assertion = await this.createAssertion();\n } catch {\n this.cryptoKey = undefined;\n throw new GscApiError({\n kind: 'auth',\n status: 0,\n googleMessage: '',\n advice:\n 'The service-account private key could not be read. Make sure GOOGLE_SERVICE_ACCOUNT_JSON contains the complete key file, including \"-----BEGIN PRIVATE KEY-----\".',\n });\n }\n\n const res = await this.fetchImpl(this.key.token_uri ?? GOOGLE_TOKEN_URL, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',\n assertion,\n }).toString(),\n });\n const body = (await res.json().catch(() => ({}))) as {\n access_token?: string;\n expires_in?: number;\n error?: string;\n error_description?: string;\n };\n if (!res.ok || !body.access_token) {\n const detail = [body.error, body.error_description].filter(Boolean).join(': ');\n throw new GscApiError({\n kind: 'auth',\n status: res.status,\n reason: body.error,\n googleMessage: detail,\n retryable: res.status >= 500,\n advice:\n body.error === 'invalid_grant'\n ? 'Google refused the service-account token (invalid_grant). Usually the key was deleted in Google Cloud, or this server’s clock is off by more than a few minutes. Create a new key and update GOOGLE_SERVICE_ACCOUNT_JSON.'\n : `Could not obtain a Google access token for the service account ${this.key.client_email}. Check that the key is valid and not disabled.`,\n });\n }\n this.cached = {\n token: body.access_token,\n expiresAt: this.now() + (body.expires_in ?? 3600) * 1000,\n };\n return body.access_token;\n }\n}\n\nasync function importPkcs8(pem: string): Promise<SigningKey> {\n const der = base64ToBytes(\n pem.replace(/-----(BEGIN|END) PRIVATE KEY-----/g, '').replace(/\\s+/g, ''),\n );\n return crypto.subtle.importKey(\n 'pkcs8',\n der,\n { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n}\n\n/** Per-instance cache of refreshed Google access tokens, keyed by user (OAuth mode). */\nexport type UserTokenCache = Map<string, { token: string; expiresAt: number }>;\n\n/**\n * A signed-in Google user's credentials (OAuth mode). Uses the access token carried in GSClaw's own\n * token while it is fresh, then refreshes with the user's refresh token, sharing results across\n * requests on this instance.\n */\nexport class UserTokenSource implements TokenSource {\n readonly identity: Identity;\n readonly scopes: string[];\n private readonly opts: {\n sub: string;\n accessToken?: string;\n /** Seconds since epoch. */\n expiresAt?: number;\n refreshToken: string;\n refresh: (refreshToken: string) => Promise<{ accessToken: string; expiresAt: number }>;\n cache: UserTokenCache;\n now?: () => number;\n };\n private inflight?: Promise<string>;\n\n constructor(email: string, scopes: string[], opts: UserTokenSource['opts']) {\n this.identity = { kind: 'user', email };\n this.scopes = scopes;\n this.opts = opts;\n }\n\n private nowMs(): number {\n return this.opts.now ? this.opts.now() : Date.now();\n }\n\n async getAccessToken(): Promise<string> {\n const now = this.nowMs();\n const { accessToken, expiresAt, cache, sub } = this.opts;\n if (accessToken && expiresAt && expiresAt * 1000 - EXPIRY_MARGIN_MS > now) return accessToken;\n const cached = cache.get(sub);\n if (cached && cached.expiresAt * 1000 - EXPIRY_MARGIN_MS > now) return cached.token;\n this.inflight ??= this.opts\n .refresh(this.opts.refreshToken)\n .then((fresh) => {\n cache.set(sub, { token: fresh.accessToken, expiresAt: fresh.expiresAt });\n return fresh.accessToken;\n })\n .finally(() => {\n this.inflight = undefined;\n });\n return this.inflight;\n }\n\n invalidate(): void {\n this.opts.accessToken = undefined;\n this.opts.cache.delete(this.opts.sub);\n }\n}\n","","import pkg from '../../package.json' with { type: 'json' };\n\nexport const NAME = 'gsclaw';\nexport const VERSION: string = pkg.version;\nexport const REPO_URL = 'https://github.com/tuyakhov/gsclaw';\n","import type { ConfigIssue } from './config.js';\nimport { REPO_URL, VERSION } from './version.js';\n\nexport const escape = (s: string) =>\n s.replace(\n /[&<>\"']/g,\n (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c]!,\n );\n\nexport const HTML_SECURITY_HEADERS: Record<string, string> = {\n 'content-type': 'text/html; charset=utf-8',\n 'content-security-policy':\n \"default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'self'; frame-ancestors 'none'\",\n 'x-content-type-options': 'nosniff',\n 'referrer-policy': 'no-referrer',\n 'x-frame-options': 'DENY',\n 'cache-control': 'no-store',\n};\n\nexport function layout(title: string, body: string): string {\n return `<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta name=\"robots\" content=\"noindex\"><title>${escape(title)}</title>\n<style>\n:root{color-scheme:light dark;--fg:#1c1b1a;--muted:#6b6862;--bg:#faf9f7;--card:#fff;--line:#e6e3de;--accent:#0d9488;--accent-text:#fff}\n@media (prefers-color-scheme:dark){:root{--fg:#ecebe8;--muted:#a19e98;--bg:#151413;--card:#1e1d1b;--line:#33312e;--accent:#2dd4bf;--accent-text:#062b27}}\nbody{margin:0;font:16px/1.55 system-ui,-apple-system,\"Segoe UI\",sans-serif;background:var(--bg);color:var(--fg);display:grid;place-items:center;min-height:100vh;padding:16px;box-sizing:border-box}\nmain{max-width:560px;width:100%;background:var(--card);border:1px solid var(--line);border-radius:14px;padding:28px}\nh1{font-size:1.25rem;margin:0 0 .5rem}p{margin:.5rem 0;color:var(--muted)}a{color:var(--accent)}\nul{padding-left:1.2rem}li{margin:.35rem 0}code{font-size:.9em}\nform{display:grid;gap:10px;margin-top:16px}label{font-weight:600;font-size:.9rem}\ninput{font:inherit;height:40px;padding:0 12px;border:1px solid var(--line);border-radius:8px;background:var(--bg);color:var(--fg)}\n.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:4px}\nbutton,.button{font:inherit;font-weight:600;height:40px;border-radius:8px;padding:0 16px;border:1px solid var(--line);background:var(--card);color:var(--fg);cursor:pointer;text-decoration:none;display:inline-flex;align-items:center}\n.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-text)}\n.client{background:var(--bg);border:1px solid var(--line);border-radius:10px;padding:12px 14px;margin:14px 0;color:var(--fg)}\n.client small{display:block;color:var(--muted)}.warn{color:#b45309}.error{color:#b91c1c;margin:0}\n</style></head><body><main>${body}</main></body></html>`;\n}\n\n/** Minimal public page: confirms the server runs, reveals nothing about its configuration. */\nexport function runningPage(): string {\n return layout(\n 'GSClaw',\n `<h1>GSClaw is running</h1>\n<p>This is a Google Search Console MCP server. Connect it from your AI client; the endpoint requires authentication.</p>\n<p><a href=\"${REPO_URL}#readme\">Documentation</a></p>`,\n );\n}\n\n/** Shown on serverless deployments that are missing configuration (names only, never values). */\nexport function setupPage(issues: ConfigIssue[]): string {\n const items = issues\n .map(\n (i) =>\n `<li>${i.variable ? `<code>${escape(i.variable)}</code> ` : ''}${escape(i.message)}</li>`,\n )\n .join('');\n return layout(\n 'GSClaw setup required',\n `<h1>GSClaw needs configuration</h1>\n<p>This deployment is not configured yet, so it refuses to serve requests. Set these environment variables in your hosting dashboard and redeploy:</p>\n<ul>${items}</ul>\n<p><a href=\"${REPO_URL}/blob/main/docs/setup.md\">Setup guide</a> · v${escape(VERSION)}</p>`,\n );\n}\n","/** GSClaw's own OAuth scopes (what a client may do), mapped to Google scopes internally. */\nexport const SCOPE_READ = 'gsc:read';\nexport const SCOPE_WRITE = 'gsc:write';\n\n/** Who is making a request, however they authenticated. */\nexport interface Principal {\n /** \"owner\" = the deployment owner (service-account mode); \"user\" = a Google user (OAuth mode). */\n kind: 'owner' | 'user';\n /** Stable subject: \"owner\", or the Google account's `sub`. */\n sub: string;\n email?: string;\n /** Granted GSClaw scopes. */\n scopes: string[];\n via: 'static_token' | 'secret_path' | 'oauth' | 'session' | 'stdio';\n /** OAuth client the token was issued to, if any. */\n clientId?: string;\n clientName?: string;\n /** The user's Google credentials (OAuth mode only). */\n google?: {\n accessToken?: string;\n /** Seconds since epoch. */\n expiresAt?: number;\n refreshToken: string;\n scope: string[];\n };\n}\n\nexport const OWNER: Principal = {\n kind: 'owner',\n sub: 'owner',\n scopes: [SCOPE_READ, SCOPE_WRITE],\n via: 'static_token',\n};\n","/**\n * Per-key token bucket (in memory, per instance). Refills `perMinute` tokens per minute with a\n * burst of the same size. A limit of 0 disables limiting.\n */\nexport class RateLimiter {\n private readonly buckets = new Map<string, { tokens: number; updated: number }>();\n\n constructor(\n private readonly perMinute: number,\n private readonly now: () => number = Date.now,\n ) {}\n\n check(key: string): { ok: true } | { ok: false; retryAfterSeconds: number } {\n if (this.perMinute <= 0) return { ok: true };\n const now = this.now();\n const ratePerMs = this.perMinute / 60_000;\n const bucket = this.buckets.get(key) ?? { tokens: this.perMinute, updated: now };\n bucket.tokens = Math.min(this.perMinute, bucket.tokens + (now - bucket.updated) * ratePerMs);\n bucket.updated = now;\n if (bucket.tokens >= 1) {\n bucket.tokens -= 1;\n this.buckets.set(key, bucket);\n this.prune(now);\n return { ok: true };\n }\n this.buckets.set(key, bucket);\n return { ok: false, retryAfterSeconds: Math.ceil((1 - bucket.tokens) / ratePerMs / 1000) };\n }\n\n private prune(now: number): void {\n if (this.buckets.size <= 1000) return;\n for (const [key, bucket] of this.buckets)\n if (now - bucket.updated > 120_000) this.buckets.delete(key);\n }\n}\n","// Dashboard session cookies. The value is a sealed blob (see seal.ts / oauth/server.ts); these\n// helpers only deal with the cookie itself.\n\nexport const SESSION_COOKIE = 'gsclaw_session';\nexport const SESSION_MAX_AGE_SECONDS = 7 * 24 * 3600;\n\nexport function parseCookies(header: string | null): Record<string, string> {\n const out: Record<string, string> = {};\n if (!header) return out;\n for (const part of header.split(';')) {\n const i = part.indexOf('=');\n if (i < 0) continue;\n const name = part.slice(0, i).trim();\n if (name) out[name] = part.slice(i + 1).trim();\n }\n return out;\n}\n\n/**\n * httpOnly + SameSite=Strict always. `Secure` everywhere except plain-http loopback development,\n * where some browsers would otherwise drop the cookie. Strict is enough even after the cross-site\n * return from Google sign-in: the HTML shell needs no cookie, and every API call is same-origin.\n */\nexport function sessionCookie(value: string, opts: { secure: boolean; maxAge?: number }): string {\n const attrs = [\n `${SESSION_COOKIE}=${value}`,\n 'Path=/',\n 'HttpOnly',\n 'SameSite=Strict',\n `Max-Age=${opts.maxAge ?? SESSION_MAX_AGE_SECONDS}`,\n ];\n if (opts.secure) attrs.push('Secure');\n return attrs.join('; ');\n}\n\nexport function clearedSessionCookie(secure: boolean): string {\n return sessionCookie('', { secure, maxAge: 0 });\n}\n\nexport const STATE_COOKIE = 'gsclaw_oauth_state';\n\n/**\n * Binds a Google sign-in to the browser that started it (login-CSRF protection). SameSite=Lax, not\n * Strict: it has to ride along on Google's cross-site redirect back to the callback, its only path.\n */\nexport function stateCookie(value: string, opts: { secure: boolean; maxAge: number }): string {\n const attrs = [\n `${STATE_COOKIE}=${value}`,\n 'Path=/oauth/google/callback',\n 'HttpOnly',\n 'SameSite=Lax',\n `Max-Age=${opts.maxAge}`,\n ];\n if (opts.secure) attrs.push('Secure');\n return attrs.join('; ');\n}\n","// OAuth clients without a database: Dynamic Client Registration hands out client IDs that are\n// sealed blobs of the client's metadata, and Client ID Metadata Documents (CIMD) are fetched from\n// the client's own URL. Redirect URIs are restricted to known MCP clients and loopback by default.\nimport type { Sealer } from '../seal.js';\n\nexport interface OAuthClient {\n clientId: string;\n name: string;\n redirectUris: string[];\n /** \"dcr\" = registered here; \"cimd\" = metadata document at the client_id URL. */\n type: 'dcr' | 'cimd';\n}\n\n/** Redirect hosts of popular remote-MCP clients. Extend with GSCLAW_OAUTH_REDIRECT_HOSTS. */\nexport const DEFAULT_REDIRECT_HOSTS = [\n 'claude.ai',\n 'claude.com',\n 'chatgpt.com',\n 'cursor.com',\n 'www.cursor.com',\n 'vscode.dev',\n 'insiders.vscode.dev',\n];\n\nconst LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]']);\nconst DCR_TTL_SECONDS = 10 * 365 * 24 * 3600;\n\nexport class OAuthError extends Error {\n constructor(\n readonly error: string,\n message: string,\n readonly status = 400,\n ) {\n super(message);\n }\n}\n\n/**\n * Loopback http redirects (desktop and CLI clients) are always allowed; https redirects must go to\n * a known client host or one listed in GSCLAW_OAUTH_REDIRECT_HOSTS (\"*\" allows any https host).\n */\nexport function redirectUriAllowed(uri: string, extraHosts: string[]): boolean {\n let url: URL;\n try {\n url = new URL(uri);\n } catch {\n return false;\n }\n if (url.hash || url.username || url.password) return false;\n if (url.protocol === 'http:') return LOOPBACK.has(url.hostname);\n if (url.protocol !== 'https:') return false;\n if (extraHosts.includes('*')) return true;\n return [...DEFAULT_REDIRECT_HOSTS, ...extraHosts].includes(url.hostname.toLowerCase());\n}\n\n/** Loopback redirect URIs match regardless of port (RFC 8252 §7.3). */\nexport function redirectUriMatches(registered: string, requested: string): boolean {\n if (registered === requested) return true;\n try {\n const a = new URL(registered);\n const b = new URL(requested);\n return (\n a.protocol === 'http:' &&\n LOOPBACK.has(a.hostname) &&\n a.hostname === b.hostname &&\n a.protocol === b.protocol &&\n a.pathname === b.pathname &&\n a.search === b.search\n );\n } catch {\n return false;\n }\n}\n\ninterface SealedClient {\n n: string;\n r: string[];\n}\n\n/** RFC 7591 registration. The returned client_id *is* the registration. */\nexport async function registerClient(\n body: unknown,\n sealer: Sealer,\n extraHosts: string[],\n): Promise<Record<string, unknown>> {\n const meta = (body && typeof body === 'object' ? body : {}) as Record<string, unknown>;\n const requested = meta.redirect_uris;\n if (!Array.isArray(requested) || requested.length === 0 || requested.length > 10) {\n throw new OAuthError('invalid_redirect_uri', 'redirect_uris must be a non-empty array.');\n }\n const redirectUris: string[] = [];\n for (const uri of requested as unknown[]) {\n if (typeof uri !== 'string' || !redirectUriAllowed(uri, extraHosts)) {\n throw new OAuthError(\n 'invalid_redirect_uri',\n `Redirect URI not allowed: ${String(uri).slice(0, 200)}. Use a loopback URI or a known client; the deployment owner can allow more hosts with GSCLAW_OAUTH_REDIRECT_HOSTS.`,\n );\n }\n redirectUris.push(uri);\n }\n const authMethod = meta.token_endpoint_auth_method ?? 'none';\n if (authMethod !== 'none') {\n throw new OAuthError(\n 'invalid_client_metadata',\n 'Only public clients are supported (token_endpoint_auth_method \"none\").',\n );\n }\n const name =\n typeof meta.client_name === 'string' && meta.client_name.trim()\n ? meta.client_name.trim().slice(0, 100)\n : 'MCP client';\n const clientId = await sealer.seal(\n 'client',\n { n: name, r: redirectUris } satisfies SealedClient,\n DCR_TTL_SECONDS,\n );\n return {\n client_id: clientId,\n client_id_issued_at: Math.floor(Date.now() / 1000),\n client_name: name,\n redirect_uris: redirectUris,\n grant_types: ['authorization_code', 'refresh_token'],\n response_types: ['code'],\n token_endpoint_auth_method: 'none',\n };\n}\n\nconst cimdCache = new Map<string, { client: OAuthClient; expiresAt: number }>();\n\n/** Refuses obviously internal targets. Workers cannot resolve DNS, so hostnames are allowed. */\nfunction publicHttpsUrl(raw: string): URL | null {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n return null;\n }\n if (url.protocol !== 'https:' || url.username || url.password || url.hash) return null;\n const host = url.hostname.toLowerCase();\n if (\n LOOPBACK.has(host) ||\n host.endsWith('.localhost') ||\n host.endsWith('.internal') ||\n host.endsWith('.local')\n )\n return null;\n if (/^\\[/.test(host)) return null; // IPv6 literals\n const ipv4 = /^(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)$/.exec(host);\n if (ipv4) {\n const [a, b] = [Number(ipv4[1]), Number(ipv4[2])];\n if (\n a === 10 ||\n a === 127 ||\n a === 0 ||\n (a === 169 && b === 254) ||\n (a === 172 && b >= 16 && b <= 31) ||\n (a === 192 && b === 168)\n ) {\n return null;\n }\n }\n return url;\n}\n\n/** Fetches and validates a Client ID Metadata Document, cached for 10 minutes per instance. */\nasync function resolveCimd(\n clientId: string,\n fetchImpl: typeof fetch,\n extraHosts: string[],\n): Promise<OAuthClient> {\n const cached = cimdCache.get(clientId);\n if (cached && cached.expiresAt > Date.now()) return cached.client;\n const url = publicHttpsUrl(clientId);\n if (!url) throw new OAuthError('invalid_client', 'client_id URL must be a public https URL.');\n let doc: Record<string, unknown>;\n try {\n const res = await fetchImpl(url.href, {\n headers: { accept: 'application/json' },\n redirect: 'error',\n signal: AbortSignal.timeout(5000),\n });\n const text = await res.text();\n if (!res.ok || text.length > 65_536) throw new Error(`HTTP ${res.status}`);\n doc = JSON.parse(text) as Record<string, unknown>;\n } catch {\n throw new OAuthError('invalid_client', 'Could not fetch the client metadata document.');\n }\n if (doc.client_id !== clientId)\n throw new OAuthError('invalid_client', 'Client metadata client_id does not match its URL.');\n const redirectUris = Array.isArray(doc.redirect_uris)\n ? doc.redirect_uris.filter((u): u is string => typeof u === 'string')\n : [];\n if (redirectUris.length === 0)\n throw new OAuthError('invalid_client', 'Client metadata has no redirect_uris.');\n if (typeof doc.client_name !== 'string' || !doc.client_name.trim()) {\n throw new OAuthError('invalid_client', 'Client metadata has no client_name.');\n }\n const client: OAuthClient = {\n clientId,\n name: doc.client_name.trim().slice(0, 100),\n redirectUris: redirectUris.filter((u) => redirectUriAllowed(u, extraHosts)),\n type: 'cimd',\n };\n cimdCache.set(clientId, { client, expiresAt: Date.now() + 10 * 60_000 });\n return client;\n}\n\n/** Looks up a client by ID: a sealed DCR registration or a CIMD URL. */\nexport async function resolveClient(\n clientId: string,\n deps: { sealer: Sealer; fetch: typeof fetch; extraHosts: string[] },\n): Promise<OAuthClient> {\n if (clientId.startsWith('https://')) return resolveCimd(clientId, deps.fetch, deps.extraHosts);\n const sealed = await deps.sealer.open<SealedClient>('client', clientId);\n if (!sealed)\n throw new OAuthError(\n 'invalid_client',\n 'Unknown client. Re-register the client (remove and re-add the connector).',\n 401,\n );\n return {\n clientId,\n name: sealed.n,\n redirectUris: sealed.r.filter((u) => redirectUriAllowed(u, deps.extraHosts)),\n type: 'dcr',\n };\n}\n\nexport function clearClientCache(): void {\n cimdCache.clear();\n}\n","// Google's OAuth 2.0 web-server flow (with PKCE), used by OAuth mode to learn who the user is and\n// obtain a refresh token for Search Console on their behalf.\nimport type { GoogleOAuthConfig } from '../config.js';\nimport { base64ToBytes } from '../crypto.js';\nimport { GscApiError } from '../errors.js';\n\nexport const GOOGLE_AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';\nexport const GOOGLE_TOKEN_URL = 'https://oauth2.googleapis.com/token';\n\nexport interface GoogleTokens {\n accessToken: string;\n /** Seconds since epoch. */\n expiresAt: number;\n refreshToken?: string;\n scope: string[];\n idToken?: string;\n}\n\nexport interface GoogleIdentity {\n sub: string;\n email: string;\n emailVerified: boolean;\n}\n\nexport function googleAuthUrl(\n cfg: GoogleOAuthConfig,\n opts: {\n redirectUri: string;\n scopes: string[];\n state: string;\n codeChallenge: string;\n loginHint?: string;\n },\n): string {\n const params = new URLSearchParams({\n client_id: cfg.clientId,\n redirect_uri: opts.redirectUri,\n response_type: 'code',\n scope: ['openid', 'email', ...opts.scopes].join(' '),\n access_type: 'offline',\n // Always ask, so Google returns a refresh token even for returning users.\n prompt: 'consent select_account',\n include_granted_scopes: 'false',\n state: opts.state,\n code_challenge: opts.codeChallenge,\n code_challenge_method: 'S256',\n });\n if (opts.loginHint) params.set('login_hint', opts.loginHint);\n return `${GOOGLE_AUTH_URL}?${params.toString()}`;\n}\n\ninterface TokenResponse {\n access_token?: string;\n expires_in?: number;\n refresh_token?: string;\n scope?: string;\n id_token?: string;\n error?: string;\n error_description?: string;\n}\n\nasync function tokenRequest(\n fetchImpl: typeof fetch,\n body: Record<string, string>,\n nowSeconds: number,\n): Promise<GoogleTokens> {\n const res = await fetchImpl(GOOGLE_TOKEN_URL, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams(body).toString(),\n });\n const data = (await res.json().catch(() => ({}))) as TokenResponse;\n if (!res.ok || !data.access_token) {\n throw new GscApiError({\n kind: 'auth',\n status: res.status,\n reason: data.error,\n googleMessage: [data.error, data.error_description].filter(Boolean).join(': '),\n advice:\n data.error === 'invalid_grant'\n ? 'Your Google authorization has expired or was revoked. Sign in to GSClaw again.'\n : 'Google rejected the sign-in. Check GOOGLE_OAUTH_CLIENT_ID / GOOGLE_OAUTH_CLIENT_SECRET and the authorized redirect URI.',\n });\n }\n return {\n accessToken: data.access_token,\n expiresAt: nowSeconds + (data.expires_in ?? 3600),\n refreshToken: data.refresh_token,\n scope: (data.scope ?? '').split(' ').filter(Boolean),\n idToken: data.id_token,\n };\n}\n\nexport function exchangeGoogleCode(\n cfg: GoogleOAuthConfig,\n opts: {\n code: string;\n redirectUri: string;\n codeVerifier: string;\n fetch: typeof fetch;\n nowSeconds: number;\n },\n): Promise<GoogleTokens> {\n return tokenRequest(\n opts.fetch,\n {\n grant_type: 'authorization_code',\n code: opts.code,\n redirect_uri: opts.redirectUri,\n client_id: cfg.clientId,\n client_secret: cfg.clientSecret,\n code_verifier: opts.codeVerifier,\n },\n opts.nowSeconds,\n );\n}\n\nexport function refreshGoogleToken(\n cfg: GoogleOAuthConfig,\n opts: { refreshToken: string; fetch: typeof fetch; nowSeconds: number },\n): Promise<GoogleTokens> {\n return tokenRequest(\n opts.fetch,\n {\n grant_type: 'refresh_token',\n refresh_token: opts.refreshToken,\n client_id: cfg.clientId,\n client_secret: cfg.clientSecret,\n },\n opts.nowSeconds,\n );\n}\n\n/**\n * Reads the identity from Google's ID token. The token was received directly from Google's token\n * endpoint over TLS (not via the browser), so OpenID Connect allows skipping signature validation;\n * we still check issuer, audience and expiry.\n */\nexport function identityFromIdToken(\n idToken: string | undefined,\n cfg: GoogleOAuthConfig,\n nowSeconds: number,\n): GoogleIdentity | null {\n if (!idToken) return null;\n try {\n const payload = JSON.parse(\n new TextDecoder().decode(base64ToBytes(idToken.split('.')[1] ?? '')),\n ) as {\n iss?: string;\n aud?: string;\n exp?: number;\n sub?: string;\n email?: string;\n email_verified?: boolean;\n };\n if (payload.iss !== 'https://accounts.google.com' && payload.iss !== 'accounts.google.com')\n return null;\n if (payload.aud !== cfg.clientId) return null;\n if (!payload.exp || payload.exp < nowSeconds - 300) return null;\n if (!payload.sub || !payload.email) return null;\n return {\n sub: payload.sub,\n email: payload.email.toLowerCase(),\n emailVerified: payload.email_verified === true,\n };\n } catch {\n return null;\n }\n}\n\n/** True when the allowlists permit this identity (no lists configured = everyone). */\nexport function isAllowed(identity: GoogleIdentity, cfg: GoogleOAuthConfig): boolean {\n if (cfg.allowedEmails.length === 0 && cfg.allowedDomains.length === 0) return true;\n if (!identity.emailVerified) return false;\n if (cfg.allowedEmails.includes(identity.email)) return true;\n const domain = identity.email.split('@')[1] ?? '';\n return cfg.allowedDomains.includes(domain);\n}\n","import { HTML_SECURITY_HEADERS, escape, layout } from '../pages.js';\n\nexport interface ConsentInfo {\n clientName: string;\n redirectUri: string;\n /** Sealed authorization request, posted back with the decision. */\n request: string;\n canWrite: boolean;\n error?: string;\n}\n\nfunction hostOf(uri: string): string {\n try {\n const url = new URL(uri);\n return url.protocol === 'http:' ? `${url.hostname} (an app on this computer)` : url.host;\n } catch {\n return uri;\n }\n}\n\n/**\n * HTML headers for consent pages. Browsers apply `form-action` to the redirect that follows a form\n * submission, so the client's redirect origin must be allowed alongside 'self'.\n */\nexport function consentHeaders(\n redirectUri: string,\n extraOrigins: string[] = [],\n): Record<string, string> {\n const origins = [...extraOrigins];\n try {\n origins.push(new URL(redirectUri).origin);\n } catch {\n // keep 'self' only\n }\n return {\n ...HTML_SECURITY_HEADERS,\n 'content-security-policy': HTML_SECURITY_HEADERS['content-security-policy']!.replace(\n \"form-action 'self'\",\n [\"form-action 'self'\", ...origins].join(' '),\n ),\n };\n}\n\nfunction clientBlock(info: ConsentInfo): string {\n return `<div class=\"client\"><strong>${escape(info.clientName)}</strong>\n<small>will receive access at ${escape(hostOf(info.redirectUri))}</small></div>`;\n}\n\nfunction access(info: ConsentInfo): string {\n return info.canWrite\n ? 'read your Search Console data and submit or remove sitemaps'\n : 'read your Search Console data (read-only)';\n}\n\n/** Service-account mode: the owner confirms with the deployment's access token. */\nexport function ownerConsentPage(info: ConsentInfo): string {\n return layout(\n 'Connect to GSClaw',\n `<h1>Allow access to GSClaw?</h1>\n${clientBlock(info)}\n<p>This app wants to ${access(info)} through this GSClaw deployment. Only continue if you started this connection.</p>\n<form method=\"post\" action=\"/oauth/authorize\">\n<input type=\"hidden\" name=\"request\" value=\"${escape(info.request)}\">\n<label for=\"token\">Access token</label>\n<input id=\"token\" name=\"token\" type=\"password\" autocomplete=\"current-password\" required autofocus>\n${info.error ? `<p class=\"error\" role=\"alert\">${escape(info.error)}</p>` : ''}\n<div class=\"actions\">\n<button class=\"primary\" type=\"submit\" name=\"decision\" value=\"allow\">Allow</button>\n<button type=\"submit\" name=\"decision\" value=\"deny\" formnovalidate>Deny</button>\n</div>\n</form>`,\n );\n}\n\n/** OAuth mode: confirm the client, then continue to Google sign-in. */\nexport function googleConsentPage(info: ConsentInfo): string {\n return layout(\n 'Connect to GSClaw',\n `<h1>Allow access to GSClaw?</h1>\n${clientBlock(info)}\n<p>This app wants to ${access(info)} through this GSClaw deployment. Sign in with the Google account that has access to your Search Console properties.</p>\n<form method=\"post\" action=\"/oauth/authorize\">\n<input type=\"hidden\" name=\"request\" value=\"${escape(info.request)}\">\n<div class=\"actions\">\n<button class=\"primary\" type=\"submit\" name=\"decision\" value=\"allow\">Continue with Google</button>\n<button type=\"submit\" name=\"decision\" value=\"deny\">Deny</button>\n</div>\n</form>`,\n );\n}\n\nexport function oauthErrorPage(title: string, message: string): string {\n return layout('GSClaw sign-in', `<h1>${escape(title)}</h1><p>${escape(message)}</p>`);\n}\n","// GSClaw's built-in OAuth 2.1 authorization server for MCP clients (claude.ai, ChatGPT, Claude Code,\n// Cursor, VS Code…). Stateless: every artifact (client registration, authorization code, access and\n// refresh token, Google round-trip state) is a sealed, expiring blob.\n//\n// Service-account mode: the owner approves a client by entering GSCLAW_ACCESS_TOKEN on the consent page.\n// OAuth mode: the consent page hands off to Google; tokens wrap the user's Google credentials.\nimport type { Config } from '../config.js';\nimport { base64UrlEncode, randomToken, sha256, timingSafeEqual } from '../crypto.js';\nimport { GscApiError } from '../errors.js';\nimport { SCOPE_READONLY, SCOPE_READWRITE } from '../gsc/auth.js';\nimport type { Logger } from '../log.js';\nimport { HTML_SECURITY_HEADERS } from '../pages.js';\nimport { SCOPE_READ, SCOPE_WRITE, type Principal } from '../principal.js';\nimport { RateLimiter } from '../ratelimit.js';\nimport type { Sealer } from '../seal.js';\nimport { parseCookies, sessionCookie, STATE_COOKIE, stateCookie } from '../session.js';\nimport { OAuthError, redirectUriMatches, registerClient, resolveClient } from './clients.js';\nimport {\n exchangeGoogleCode,\n googleAuthUrl,\n identityFromIdToken,\n isAllowed,\n refreshGoogleToken,\n} from './google.js';\nimport { consentHeaders, googleConsentPage, oauthErrorPage, ownerConsentPage } from './pages.js';\n\nconst REQUEST_TTL = 15 * 60;\nconst CODE_TTL = 60;\nconst ACCESS_TTL = 3600;\nconst REFRESH_TTL = 90 * 24 * 3600;\nexport const SESSION_TTL = 7 * 24 * 3600;\n\n/** Compact principal stored inside sealed tokens. */\ninterface SealedPrincipal {\n k: 'owner' | 'user';\n sub: string;\n email?: string;\n /** Google verified the email (allowlists only ever match verified emails). */\n ev?: boolean;\n g?: { at?: string; ex?: number; rt: string; sc: string[] };\n}\n\n/** A validated authorization request, sealed into the consent form and the Google state. */\ninterface AuthRequest {\n cid: string;\n cn: string;\n ru: string;\n cc: string;\n st?: string;\n sc: string[];\n res: string;\n}\n\ninterface GoogleState {\n req?: AuthRequest;\n dash?: boolean;\n /** PKCE verifier for the Google round-trip. */\n v: string;\n /** Browser binding: must match the state cookie set when the round-trip started. */\n b: string;\n}\n\ninterface CodePayload extends Omit<AuthRequest, 'st'> {\n p: SealedPrincipal;\n}\n\ninterface TokenPayload {\n cid: string;\n cn: string;\n sc: string[];\n aud: string;\n p: SealedPrincipal;\n}\n\nexport interface SessionPayload {\n p: SealedPrincipal;\n}\n\nexport interface OAuthServerDeps {\n config: Config;\n sealer: Sealer;\n logger: Logger;\n fetch: typeof fetch;\n now: () => Date;\n}\n\nconst CORS = {\n 'access-control-allow-origin': '*',\n 'access-control-allow-methods': 'GET, POST, OPTIONS',\n 'access-control-allow-headers': 'authorization, content-type, mcp-protocol-version',\n};\n\nfunction json(body: unknown, status = 200, headers: Record<string, string> = {}): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: {\n 'content-type': 'application/json',\n 'cache-control': 'no-store',\n pragma: 'no-cache',\n ...CORS,\n ...headers,\n },\n });\n}\n\nfunction html(\n body: string,\n status = 200,\n headers: Record<string, string> = HTML_SECURITY_HEADERS,\n): Response {\n return new Response(body, { status, headers });\n}\n\nfunction redirect(location: string, headers: Record<string, string> = {}): Response {\n return new Response(null, {\n status: 302,\n headers: { location, 'cache-control': 'no-store', ...headers },\n });\n}\n\nfunction withParams(uri: string, params: Record<string, string | undefined>): string {\n const url = new URL(uri);\n for (const [k, v] of Object.entries(params)) if (v !== undefined) url.searchParams.set(k, v);\n return url.href;\n}\n\nasync function readForm(request: Request): Promise<Record<string, string>> {\n const type = request.headers.get('content-type') ?? '';\n const text = await request.text();\n if (type.includes('application/json')) {\n try {\n const body = JSON.parse(text) as Record<string, unknown>;\n return Object.fromEntries(\n Object.entries(body).map(([k, v]) => [k, typeof v === 'string' ? v : String(v)]),\n );\n } catch {\n return {};\n }\n }\n return Object.fromEntries(new URLSearchParams(text));\n}\n\nexport function toPrincipal(\n p: SealedPrincipal,\n extra: Pick<Principal, 'scopes' | 'via' | 'clientId' | 'clientName'>,\n): Principal {\n return {\n kind: p.k,\n sub: p.sub,\n email: p.email,\n ...extra,\n google: p.g\n ? { accessToken: p.g.at, expiresAt: p.g.ex, refreshToken: p.g.rt, scope: p.g.sc }\n : undefined,\n };\n}\n\nexport function createOAuthServer(deps: OAuthServerDeps) {\n const { config, sealer, logger } = deps;\n const nowSeconds = () => Math.floor(deps.now().getTime() / 1000);\n const consentLimiter = new RateLimiter(10);\n const scopesSupported = config.allowWrites ? [SCOPE_READ, SCOPE_WRITE] : [SCOPE_READ];\n const google = config.googleOAuth;\n const clientDeps = { sealer, fetch: deps.fetch, extraHosts: config.oauthRedirectHosts };\n\n const resourceUrl = (base: string) => `${base}/mcp`;\n const metadataUrl = (base: string) => `${base}/.well-known/oauth-protected-resource/mcp`;\n\n function grantedScopes(requested: string | undefined | null): string[] {\n const wanted = (requested ?? '').split(/\\s+/).filter((s) => scopesSupported.includes(s));\n const scopes = new Set(wanted.length > 0 ? wanted : [SCOPE_READ]);\n scopes.add(SCOPE_READ);\n return [...scopes];\n }\n\n const googleScopeFor = (scopes: string[]) =>\n scopes.includes(SCOPE_WRITE) ? SCOPE_READWRITE : SCOPE_READONLY;\n\n function protectedResourceMetadata(base: string) {\n return {\n resource: resourceUrl(base),\n authorization_servers: [base],\n scopes_supported: scopesSupported,\n bearer_methods_supported: ['header'],\n resource_name: 'GSClaw',\n resource_documentation: 'https://github.com/tuyakhov/gsclaw#readme',\n };\n }\n\n function authorizationServerMetadata(base: string) {\n return {\n issuer: base,\n authorization_endpoint: `${base}/oauth/authorize`,\n token_endpoint: `${base}/oauth/token`,\n registration_endpoint: `${base}/oauth/register`,\n scopes_supported: scopesSupported,\n response_types_supported: ['code'],\n response_modes_supported: ['query'],\n grant_types_supported: ['authorization_code', 'refresh_token'],\n token_endpoint_auth_methods_supported: ['none'],\n code_challenge_methods_supported: ['S256'],\n client_id_metadata_document_supported: true,\n authorization_response_iss_parameter_supported: true,\n service_documentation: 'https://github.com/tuyakhov/gsclaw#readme',\n };\n }\n\n /**\n * The allowlist applies to every request, not just sign-in, so removing someone from\n * ALLOWED_GOOGLE_EMAILS/DOMAINS (and redeploying) cuts off their existing tokens and sessions too.\n */\n const stillAllowed = (p: SealedPrincipal): boolean =>\n p.k !== 'user' ||\n !google ||\n isAllowed({ sub: p.sub, email: p.email ?? '', emailVerified: p.ev === true }, google);\n\n async function issueCode(req: AuthRequest, p: SealedPrincipal, base: string): Promise<Response> {\n const code = await sealer.seal(\n 'code',\n {\n cid: req.cid,\n cn: req.cn,\n ru: req.ru,\n cc: req.cc,\n sc: req.sc,\n res: req.res,\n p,\n } satisfies CodePayload,\n CODE_TTL,\n );\n logger.info('oauth code issued', { client: req.cn, kind: p.k });\n return redirect(withParams(req.ru, { code, state: req.st, iss: base }));\n }\n\n const clientError = (req: AuthRequest, error: string, description: string, base: string) =>\n redirect(\n withParams(req.ru, { error, error_description: description, state: req.st, iss: base }),\n );\n\n async function startGoogle(\n target: Pick<GoogleState, 'req' | 'dash'>,\n scopes: string[],\n base: string,\n ): Promise<Response> {\n const state: GoogleState = { ...target, v: randomToken(32), b: randomToken(24) };\n const challenge = base64UrlEncode(await sha256(state.v));\n const sealedState = await sealer.seal('state', state, REQUEST_TTL);\n const secure = new URL(base).protocol === 'https:';\n return redirect(\n googleAuthUrl(google!, {\n redirectUri: `${base}/oauth/google/callback`,\n scopes: [googleScopeFor(scopes)],\n state: sealedState,\n codeChallenge: challenge,\n }),\n { 'set-cookie': stateCookie(state.b, { secure, maxAge: REQUEST_TTL }) },\n );\n }\n\n /** GET /oauth/authorize: validate, then show the consent page. */\n async function authorize(url: URL, base: string): Promise<Response> {\n const q = url.searchParams;\n const clientId = q.get('client_id');\n if (!clientId) return html(oauthErrorPage('Invalid request', 'client_id is missing.'), 400);\n let client;\n try {\n client = await resolveClient(clientId, clientDeps);\n } catch (error) {\n return html(\n oauthErrorPage('Unknown app', error instanceof Error ? error.message : 'Unknown client.'),\n 400,\n );\n }\n const requested =\n q.get('redirect_uri') ?? (client.redirectUris.length === 1 ? client.redirectUris[0]! : '');\n if (!client.redirectUris.some((r) => redirectUriMatches(r, requested))) {\n return html(\n oauthErrorPage('Invalid redirect', 'The redirect_uri is not registered for this app.'),\n 400,\n );\n }\n const req: AuthRequest = {\n cid: clientId,\n cn: client.name,\n ru: requested,\n cc: q.get('code_challenge') ?? '',\n st: q.get('state') ?? undefined,\n sc: grantedScopes(q.get('scope')),\n res: resourceUrl(base),\n };\n if (q.get('response_type') !== 'code')\n return clientError(\n req,\n 'unsupported_response_type',\n 'Only response_type=code is supported.',\n base,\n );\n if (!req.cc || q.get('code_challenge_method') !== 'S256') {\n return clientError(\n req,\n 'invalid_request',\n 'PKCE with code_challenge_method=S256 is required.',\n base,\n );\n }\n const resource = q.get('resource');\n if (resource && resource.replace(/\\/$/, '') !== req.res) {\n return clientError(req, 'invalid_target', `This server's resource is ${req.res}.`, base);\n }\n const sealedReq = await sealer.seal('request', req, REQUEST_TTL);\n const info = {\n clientName: client.name,\n redirectUri: req.ru,\n request: sealedReq,\n canWrite: req.sc.includes(SCOPE_WRITE),\n };\n return config.authMode === 'oauth'\n ? html(googleConsentPage(info), 200, consentHeaders(req.ru, ['https://accounts.google.com']))\n : html(ownerConsentPage(info), 200, consentHeaders(req.ru));\n }\n\n /** POST /oauth/authorize: the consent decision. */\n async function decide(request: Request, base: string): Promise<Response> {\n const form = await readForm(request);\n const req = await sealer.open<AuthRequest>('request', form.request);\n if (!req)\n return html(\n oauthErrorPage(\n 'Sign-in expired',\n 'This sign-in page expired. Start the connection again from your app.',\n ),\n 400,\n );\n if (form.decision !== 'allow')\n return clientError(req, 'access_denied', 'The request was denied.', base);\n\n if (config.authMode === 'oauth') return startGoogle({ req }, req.sc, base);\n\n const ip =\n request.headers.get('cf-connecting-ip') ??\n request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ??\n 'local';\n const info = {\n clientName: req.cn,\n redirectUri: req.ru,\n request: form.request!,\n canWrite: req.sc.includes(SCOPE_WRITE),\n };\n if (!consentLimiter.check(ip).ok) {\n return html(\n ownerConsentPage({ ...info, error: 'Too many attempts. Wait a minute and try again.' }),\n 429,\n consentHeaders(req.ru),\n );\n }\n if (!form.token || !(await timingSafeEqual(form.token.trim(), config.accessToken!))) {\n logger.warn('oauth owner consent: wrong access token');\n return html(\n ownerConsentPage({ ...info, error: 'That access token is not correct.' }),\n 401,\n consentHeaders(req.ru),\n );\n }\n return issueCode(req, { k: 'owner', sub: 'owner' }, base);\n }\n\n /** GET /oauth/google/callback (OAuth mode): finish Google sign-in for an MCP client or the dashboard. */\n async function googleCallback(request: Request, url: URL, base: string): Promise<Response> {\n const state = await sealer.open<GoogleState>('state', url.searchParams.get('state'));\n const bound = parseCookies(request.headers.get('cookie'))[STATE_COOKIE];\n if (!state || !google || !bound || !(await timingSafeEqual(bound, state.b)))\n return html(\n oauthErrorPage(\n 'Sign-in expired',\n 'This sign-in expired or was started in a different browser. Please start again.',\n ),\n 400,\n );\n const fail = (error: string, description: string, dashCode: string) =>\n state.req\n ? clientError(state.req, error, description, base)\n : redirect(`/?login_error=${dashCode}`);\n\n if (url.searchParams.get('error') || !url.searchParams.get('code'))\n return fail('access_denied', 'Google sign-in was cancelled.', 'cancelled');\n\n let tokens;\n try {\n tokens = await exchangeGoogleCode(google, {\n code: url.searchParams.get('code')!,\n redirectUri: `${base}/oauth/google/callback`,\n codeVerifier: state.v,\n fetch: deps.fetch,\n nowSeconds: nowSeconds(),\n });\n } catch (error) {\n logger.warn('google code exchange failed', { error });\n return fail('server_error', 'Google sign-in failed.', 'google_error');\n }\n const identity = identityFromIdToken(tokens.idToken, google, nowSeconds());\n if (!identity)\n return fail('server_error', 'Google did not return a verified identity.', 'google_error');\n if (!isAllowed(identity, google)) {\n logger.warn('google account not allowed', { domain: identity.email.split('@')[1] });\n return fail(\n 'access_denied',\n `${identity.email} is not allowed to use this GSClaw deployment.`,\n 'not_allowed',\n );\n }\n const needed = state.req ? googleScopeFor(state.req.sc) : SCOPE_READONLY;\n const granted =\n tokens.scope.includes(needed) ||\n (needed === SCOPE_READONLY && tokens.scope.includes(SCOPE_READWRITE));\n if (!granted)\n return fail(\n 'access_denied',\n 'Search Console access was not granted. Try again and keep the Search Console permission ticked.',\n 'scope',\n );\n if (!tokens.refreshToken) {\n return fail(\n 'server_error',\n 'Google did not return a refresh token. Remove GSClaw from your Google account’s third-party access and try again.',\n 'google_error',\n );\n }\n\n const p: SealedPrincipal = {\n k: 'user',\n sub: identity.sub,\n email: identity.email,\n ev: identity.emailVerified,\n g: {\n at: tokens.accessToken,\n ex: tokens.expiresAt,\n rt: tokens.refreshToken,\n sc: tokens.scope,\n },\n };\n if (state.dash) {\n const value = await sealer.seal('session', { p } satisfies SessionPayload, SESSION_TTL);\n const secure = new URL(base).protocol === 'https:';\n return redirect('/', { 'set-cookie': sessionCookie(value, { secure, maxAge: SESSION_TTL }) });\n }\n return issueCode(state.req!, p, base);\n }\n\n async function issueTokens(t: Omit<TokenPayload, 'p'>, p: SealedPrincipal): Promise<Response> {\n let ttl = ACCESS_TTL;\n if (p.g?.ex) ttl = Math.max(300, Math.min(ACCESS_TTL, p.g.ex - nowSeconds() - 120));\n const access = await sealer.seal('access', { ...t, p } satisfies TokenPayload, ttl);\n // The refresh token keeps only the Google refresh token; access tokens are re-derived on refresh.\n const refreshPrincipal: SealedPrincipal = p.g ? { ...p, g: { rt: p.g.rt, sc: p.g.sc } } : p;\n const refresh = await sealer.seal(\n 'refresh',\n { ...t, p: refreshPrincipal } satisfies TokenPayload,\n REFRESH_TTL,\n );\n return json({\n access_token: access,\n token_type: 'Bearer',\n expires_in: ttl,\n refresh_token: refresh,\n scope: t.sc.join(' '),\n });\n }\n\n /** POST /oauth/token */\n async function token(request: Request): Promise<Response> {\n const form = await readForm(request);\n const basic = request.headers.get('authorization');\n let clientId = form.client_id;\n if (!clientId && basic?.startsWith('Basic ')) {\n try {\n clientId = decodeURIComponent(atob(basic.slice(6)).split(':')[0] ?? '');\n } catch {\n clientId = undefined;\n }\n }\n const err = (error: string, description: string, status = 400) =>\n json({ error, error_description: description }, status);\n\n if (form.grant_type === 'authorization_code') {\n const code = await sealer.open<CodePayload>('code', form.code);\n if (!code) return err('invalid_grant', 'The authorization code is invalid or expired.');\n if (code.cid !== clientId)\n return err('invalid_grant', 'The code was issued to another client.');\n if (!form.redirect_uri || !redirectUriMatches(code.ru, form.redirect_uri))\n return err('invalid_grant', 'redirect_uri does not match.');\n if (!form.code_verifier || base64UrlEncode(await sha256(form.code_verifier)) !== code.cc) {\n return err('invalid_grant', 'PKCE verification failed.');\n }\n if (form.resource && form.resource.replace(/\\/$/, '') !== code.res)\n return err('invalid_target', 'Unknown resource.');\n return issueTokens({ cid: code.cid, cn: code.cn, sc: code.sc, aud: code.res }, code.p);\n }\n\n if (form.grant_type === 'refresh_token') {\n const rt = await sealer.open<TokenPayload>('refresh', form.refresh_token);\n if (!rt) return err('invalid_grant', 'The refresh token is invalid or expired.');\n if (clientId && clientId !== rt.cid)\n return err('invalid_grant', 'The refresh token was issued to another client.');\n if (!stillAllowed(rt.p))\n return err('invalid_grant', 'This Google account is no longer allowed to use GSClaw.');\n let scopes = rt.sc;\n if (form.scope) {\n const narrowed = form.scope.split(/\\s+/).filter((s) => rt.sc.includes(s));\n if (narrowed.length === 0)\n return err('invalid_scope', 'Requested scope exceeds the original grant.');\n scopes = narrowed;\n }\n let p = rt.p;\n if (p.g && google) {\n try {\n const fresh = await refreshGoogleToken(google, {\n refreshToken: p.g.rt,\n fetch: deps.fetch,\n nowSeconds: nowSeconds(),\n });\n p = {\n ...p,\n g: {\n at: fresh.accessToken,\n ex: fresh.expiresAt,\n rt: fresh.refreshToken ?? p.g.rt,\n sc: p.g.sc,\n },\n };\n } catch (error) {\n if (error instanceof GscApiError && error.reason === 'invalid_grant') {\n return err('invalid_grant', 'Google access was revoked or expired. Sign in again.');\n }\n return err(\n 'temporarily_unavailable',\n 'Could not refresh Google credentials. Try again.',\n 503,\n );\n }\n }\n return issueTokens({ cid: rt.cid, cn: rt.cn, sc: scopes, aud: rt.aud }, p);\n }\n\n return err('unsupported_grant_type', 'Use authorization_code or refresh_token.');\n }\n\n return {\n resourceUrl,\n\n /** WWW-Authenticate value for /mcp 401s (RFC 9728 discovery). */\n challenge(base: string, error?: 'invalid_token'): string {\n const parts = [\n `resource_metadata=\"${metadataUrl(base)}\"`,\n `scope=\"${scopesSupported.join(' ')}\"`,\n ];\n if (error) parts.push(`error=\"${error}\"`);\n return `Bearer ${parts.join(', ')}`;\n },\n\n /** Validates a GSClaw-issued access token for this resource. */\n async verifyAccessToken(token: string, base: string): Promise<Principal | null> {\n const t = await sealer.open<TokenPayload>('access', token);\n if (!t || t.aud !== resourceUrl(base)) return null;\n if ((config.authMode === 'oauth') !== (t.p.k === 'user') || !stillAllowed(t.p)) return null;\n return toPrincipal(t.p, { scopes: t.sc, via: 'oauth', clientId: t.cid, clientName: t.cn });\n },\n\n /** Reads a dashboard session cookie value. */\n async openSession(value: string | undefined): Promise<Principal | null> {\n const s = await sealer.open<SessionPayload>('session', value);\n if (!s) return null;\n if ((config.authMode === 'oauth') !== (s.p.k === 'user') || !stillAllowed(s.p)) return null;\n return toPrincipal(s.p, { scopes: [SCOPE_READ], via: 'session' });\n },\n\n async ownerSession(): Promise<string> {\n return sealer.seal(\n 'session',\n { p: { k: 'owner', sub: 'owner' } } satisfies SessionPayload,\n SESSION_TTL,\n );\n },\n\n /** Routes /.well-known/* and /oauth/*; returns null for anything else. */\n async handle(request: Request, url: URL, path: string, base: string): Promise<Response | null> {\n const method = request.method;\n const isWellKnown = path.startsWith('/.well-known/');\n if (!isWellKnown && !path.startsWith('/oauth/')) return null;\n if (method === 'OPTIONS') return new Response(null, { status: 204, headers: CORS });\n\n try {\n if (\n path === '/.well-known/oauth-protected-resource' ||\n path === '/.well-known/oauth-protected-resource/mcp'\n ) {\n return json(protectedResourceMetadata(base));\n }\n if (\n path === '/.well-known/oauth-authorization-server' ||\n path === '/.well-known/oauth-authorization-server/mcp'\n ) {\n return json(authorizationServerMetadata(base));\n }\n if (path === '/oauth/register' && method === 'POST') {\n const body: unknown = await request.json().catch(() => null);\n return json(await registerClient(body, sealer, config.oauthRedirectHosts), 201);\n }\n if (path === '/oauth/authorize' && method === 'GET') return authorize(url, base);\n if (path === '/oauth/authorize' && method === 'POST') return decide(request, base);\n if (path === '/oauth/token' && method === 'POST') return token(request);\n if (path === '/oauth/google/callback' && method === 'GET')\n return googleCallback(request, url, base);\n if (path === '/oauth/google/start' && method === 'GET' && google) {\n return startGoogle({ dash: true }, [SCOPE_READ], base);\n }\n } catch (error) {\n if (error instanceof OAuthError) {\n return json({ error: error.error, error_description: error.message }, error.status);\n }\n throw error;\n }\n return json({ error: 'not_found' }, 404);\n },\n };\n}\n\nexport type OAuthServer = ReturnType<typeof createOAuthServer>;\n","import { VERSION } from './version.js';\n\nconst RELEASES_URL = 'https://api.github.com/repos/tuyakhov/gsclaw/releases/latest';\nconst CACHE_MS = 6 * 3600_000;\n\nlet cached: { at: number; latest: string | null } | undefined;\n\n/** Compares dotted numeric versions; pre-release suffixes are ignored. */\nexport function isNewer(candidate: string, current: string): boolean {\n const parse = (v: string) =>\n v\n .replace(/^v/, '')\n .split('-')[0]!\n .split('.')\n .map((n) => Number(n) || 0);\n const a = parse(candidate);\n const b = parse(current);\n for (let i = 0; i < Math.max(a.length, b.length); i++) {\n const diff = (a[i] ?? 0) - (b[i] ?? 0);\n if (diff !== 0) return diff > 0;\n }\n return false;\n}\n\n/**\n * Latest published GSClaw release (cached for 6 hours per instance). Any failure (offline,\n * rate-limited, no releases yet) yields null so the dashboard simply shows no notice.\n */\nexport async function latestRelease(\n fetchImpl: typeof fetch = (input, init) => fetch(input, init),\n now = Date.now(),\n): Promise<string | null> {\n if (cached && now - cached.at < CACHE_MS) return cached.latest;\n let latest: string | null = null;\n try {\n const res = await fetchImpl(RELEASES_URL, {\n headers: { accept: 'application/vnd.github+json', 'user-agent': `gsclaw/${VERSION}` },\n signal: AbortSignal.timeout(3000),\n });\n if (res.ok) {\n const body = (await res.json()) as { tag_name?: unknown };\n if (typeof body.tag_name === 'string') latest = body.tag_name.replace(/^v/, '');\n }\n } catch {\n latest = null;\n }\n cached = { at: now, latest };\n return latest;\n}\n\nexport function resetUpdateCheckCache(): void {\n cached = undefined;\n}\n","import type { Config } from './config.js';\nimport { timingSafeEqual } from './crypto.js';\nimport { publicErrorMessage } from './errors.js';\nimport { listProperties } from './gsc/sites.js';\nimport type { Logger } from './log.js';\nimport type { OAuthServer } from './oauth/server.js';\nimport { SESSION_TTL } from './oauth/server.js';\nimport type { Principal } from './principal.js';\nimport { RateLimiter } from './ratelimit.js';\nimport type { Runtime } from './runtime.js';\nimport { clearedSessionCookie, parseCookies, SESSION_COOKIE, sessionCookie } from './session.js';\nimport { isNewer, latestRelease } from './update-check.js';\nimport { VERSION } from './version.js';\n\nexport interface DashboardApiDeps {\n config: Config;\n runtime: Runtime;\n oauth: OAuthServer;\n logger: Logger;\n platform: string;\n fetch?: typeof fetch;\n now?: () => Date;\n baseUrl: (request: Request, url: URL) => string;\n}\n\nfunction json(body: unknown, status = 200, headers: Record<string, string> = {}): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json', 'cache-control': 'no-store', ...headers },\n });\n}\n\nconst LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]']);\n\n/**\n * JSON API behind the dashboard. Every data endpoint runs the same tool implementations the MCP\n * server uses (`POST /api/tools/:name`) with the signed-in caller's own credentials, so the dashboard\n * and the AI always see identical numbers.\n */\nexport function createDashboardApi(deps: DashboardApiDeps) {\n const { config, runtime, logger, oauth } = deps;\n const isOAuth = config.authMode === 'oauth';\n const publicOrigin = config.publicBaseUrl ? new URL(config.publicBaseUrl).origin : null;\n const loginLimiter = new RateLimiter(10);\n\n const isSecure = (url: URL) => !(url.protocol === 'http:' && LOOPBACK.has(url.hostname));\n\n async function signedIn(request: Request): Promise<Principal | null> {\n return oauth.openSession(parseCookies(request.headers.get('cookie'))[SESSION_COOKIE]);\n }\n\n /** Same-origin check for state-changing requests (on top of SameSite=Strict cookies). */\n function sameOrigin(request: Request, url: URL): boolean {\n const origin = request.headers.get('origin');\n if (!origin) return true;\n return origin === url.origin || origin === publicOrigin;\n }\n\n function clientKey(request: Request): string {\n return (\n request.headers.get('cf-connecting-ip') ??\n request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ??\n 'local'\n );\n }\n\n async function readJson(request: Request): Promise<Record<string, unknown> | null> {\n if (!request.headers.get('content-type')?.includes('application/json')) return null;\n try {\n const body: unknown = await request.json();\n return body && typeof body === 'object' && !Array.isArray(body)\n ? (body as Record<string, unknown>)\n : null;\n } catch {\n return null;\n }\n }\n\n async function login(request: Request, url: URL): Promise<Response> {\n if (isOAuth) return json({ error: 'Use “Sign in with Google”.' }, 400);\n const limit = loginLimiter.check(clientKey(request));\n if (!limit.ok) {\n return json({ error: 'Too many attempts. Wait a minute and try again.' }, 429, {\n 'retry-after': String(limit.retryAfterSeconds),\n });\n }\n const body = await readJson(request);\n const token = typeof body?.token === 'string' ? body.token.trim() : '';\n if (!token || !(await timingSafeEqual(token, config.accessToken!))) {\n logger.warn('dashboard login failed');\n return json({ error: 'That access token is not correct.' }, 401);\n }\n const cookie = sessionCookie(await oauth.ownerSession(), {\n secure: isSecure(url),\n maxAge: SESSION_TTL,\n });\n return json({ ok: true }, 200, { 'set-cookie': cookie });\n }\n\n async function status(principal: Principal, request: Request, url: URL): Promise<Response> {\n const session = runtime.sessionFor(principal);\n const started = Date.now();\n let google: Record<string, unknown>;\n try {\n const properties = await listProperties(session.gsc, { fresh: true });\n google = { ok: true, latency_ms: Date.now() - started, properties };\n } catch (error) {\n const { message, kind } = publicErrorMessage(error);\n google = { ok: false, latency_ms: Date.now() - started, error: message, kind };\n }\n const latest = await latestRelease(deps.fetch);\n const base = deps.baseUrl(request, url);\n const warnings: string[] = [];\n if (!config.publicBaseUrl && !LOOPBACK.has(url.hostname)) {\n warnings.push(\n 'PUBLIC_BASE_URL is not set and could not be detected; connection snippets use this page’s address.',\n );\n }\n const g = config.googleOAuth;\n const oauthInfo = g\n ? {\n redirect_uri: `${base}/oauth/google/callback`,\n allowed_domains: g.allowedDomains,\n allowed_email_count: g.allowedEmails.length,\n open_to_anyone: g.allowedDomains.length === 0 && g.allowedEmails.length === 0,\n }\n : null;\n if (oauthInfo?.open_to_anyone) {\n warnings.push(\n 'Anyone with a Google account can sign in (each person only sees their own properties). Set ALLOWED_GOOGLE_EMAILS or ALLOWED_GOOGLE_DOMAINS to restrict access.',\n );\n }\n return json({\n version: VERSION,\n latest_version: latest,\n update_available: latest !== null && isNewer(latest, VERSION),\n platform: deps.platform,\n auth_mode: config.authMode,\n identity: session.gsc.identity,\n writes_enabled: config.allowWrites,\n secret_path_enabled: !isOAuth && config.secretPath,\n public_base_url: config.publicBaseUrl ?? null,\n rate_limit_per_minute: config.rateLimitPerMinute,\n activity_persistent: runtime.activity.persistent,\n oauth: oauthInfo,\n google,\n warnings,\n });\n }\n\n async function runTool(name: string, request: Request, principal: Principal): Promise<Response> {\n const session = runtime.sessionFor(principal);\n const tool = session.tools.find((t) => t.name === name && !t.write);\n if (!tool) return json({ error: `Unknown tool: ${name}` }, 404);\n const body = (await readJson(request)) ?? {};\n const parsed = tool.input.safeParse(body);\n if (!parsed.success) {\n const message = parsed.error.issues\n .map((i) => `${i.path.join('.')}: ${i.message}`)\n .join('; ');\n return json({ error: message }, 400);\n }\n try {\n return json({ result: await tool.run(session.ctx, parsed.data) });\n } catch (error) {\n const { message, kind } = publicErrorMessage(error);\n if (kind === 'internal') logger.error('dashboard tool failed', { tool: name, error });\n return json({ error: message, kind }, kind === 'internal' ? 500 : 400);\n }\n }\n\n return {\n async handle(request: Request, url: URL, path: string): Promise<Response> {\n const method = request.method;\n if (method !== 'GET' && method !== 'HEAD' && !sameOrigin(request, url)) {\n return json({ error: 'Cross-origin request rejected.' }, 403);\n }\n\n if (path === '/api/session') {\n if (method === 'GET') {\n const principal = await signedIn(request);\n return json({\n authenticated: principal !== null,\n auth_mode: config.authMode,\n login: isOAuth ? 'google' : 'token',\n email: principal?.email ?? null,\n });\n }\n if (method === 'POST') return login(request, url);\n if (method === 'DELETE') {\n return json({ ok: true }, 200, { 'set-cookie': clearedSessionCookie(isSecure(url)) });\n }\n return json({ error: 'Method not allowed' }, 405);\n }\n\n const principal = await signedIn(request);\n if (!principal) return json({ error: 'Sign in required.' }, 401);\n\n if (path === '/api/status' && method === 'GET') return status(principal, request, url);\n if (path === '/api/connect' && method === 'GET') {\n const base = deps.baseUrl(request, url);\n return json({\n base_url: base,\n mcp_url: `${base}/mcp`,\n auth_mode: config.authMode,\n secret_path_enabled: !isOAuth && config.secretPath,\n });\n }\n if (path === '/api/connect/secret' && method === 'GET') {\n return isOAuth\n ? json({ error: 'Not available in OAuth mode.' }, 404)\n : json({ access_token: config.accessToken });\n }\n if (path === '/api/activity' && method === 'GET') {\n // In OAuth mode each person only sees their own calls.\n const entries = runtime.activity\n .list()\n .filter((e) => !isOAuth || e.user === principal.email);\n return json({ persistent: runtime.activity.persistent, entries });\n }\n const toolMatch = /^\\/api\\/tools\\/([a-z_]+)$/.exec(path);\n if (toolMatch && method === 'POST') return runTool(toolMatch[1]!, request, principal);\n\n return json({ error: 'Not found' }, 404);\n },\n };\n}\n","// Stateless, tamper-proof tokens: JSON payloads encrypted with AES-256-GCM under keys derived (HKDF)\n// from one deployment secret, with a separate key per purpose. A sealed authorization code can\n// therefore never be replayed as an access token, a client ID, or a session.\nimport { base64ToBytes, base64UrlEncode, utf8 } from './crypto.js';\n\nexport type SealPurpose =\n 'client' | 'state' | 'request' | 'code' | 'access' | 'refresh' | 'session';\n\n/** Short visible prefixes make tokens recognisable in logs and support tickets (never their content). */\nexport const SEAL_PREFIX: Record<SealPurpose, string> = {\n client: 'gsc_client_',\n state: 'gsc_state_',\n request: 'gsc_req_',\n code: 'gsc_code_',\n access: 'gsc_at_',\n refresh: 'gsc_rt_',\n session: 'gsc_sess_',\n};\n\ntype AesKey = Awaited<ReturnType<typeof crypto.subtle.importKey>>;\n\nexport interface Sealer {\n seal(purpose: SealPurpose, payload: object, ttlSeconds: number): Promise<string>;\n /** Returns null for anything malformed, tampered with, of another purpose, or expired. */\n open<T extends object>(\n purpose: SealPurpose,\n token: string | null | undefined,\n ): Promise<(T & { exp: number }) | null>;\n}\n\nexport function createSealer(secret: string, now: () => number = Date.now): Sealer {\n const keys = new Map<SealPurpose, Promise<AesKey>>();\n\n const keyFor = (purpose: SealPurpose): Promise<AesKey> => {\n let key = keys.get(purpose);\n if (!key) {\n key = (async () => {\n const material = await crypto.subtle.importKey('raw', utf8(secret), 'HKDF', false, [\n 'deriveKey',\n ]);\n return crypto.subtle.deriveKey(\n {\n name: 'HKDF',\n hash: 'SHA-256',\n salt: utf8('gsclaw-seal-v1'),\n info: utf8(`gsclaw:${purpose}`),\n },\n material,\n { name: 'AES-GCM', length: 256 },\n false,\n ['encrypt', 'decrypt'],\n );\n })();\n keys.set(purpose, key);\n }\n return key;\n };\n\n return {\n async seal(purpose, payload, ttlSeconds) {\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const body = utf8(JSON.stringify({ ...payload, exp: Math.floor(now() / 1000) + ttlSeconds }));\n const cipher = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: 'AES-GCM', iv, additionalData: utf8(purpose) },\n await keyFor(purpose),\n body,\n ),\n );\n const out = new Uint8Array(iv.length + cipher.length);\n out.set(iv);\n out.set(cipher, iv.length);\n return SEAL_PREFIX[purpose] + base64UrlEncode(out);\n },\n\n async open<T extends object>(purpose: SealPurpose, token: string | null | undefined) {\n const prefix = SEAL_PREFIX[purpose];\n if (!token?.startsWith(prefix) || token.length > 8192) return null;\n try {\n const bytes = base64ToBytes(token.slice(prefix.length));\n if (bytes.length < 13 + 16) return null;\n const plain = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: bytes.subarray(0, 12), additionalData: utf8(purpose) },\n await keyFor(purpose),\n bytes.subarray(12),\n );\n const payload = JSON.parse(new TextDecoder().decode(plain)) as T & { exp: number };\n if (typeof payload.exp !== 'number' || payload.exp <= Math.floor(now() / 1000)) return null;\n return payload;\n } catch {\n return null;\n }\n },\n };\n}\n","import { VERSION } from './version.js';\n\n/** Strict CSP: the dashboard only loads its own bundle and talks to its own API. */\nexport const DASHBOARD_HEADERS: Record<string, string> = {\n 'content-type': 'text/html; charset=utf-8',\n 'content-security-policy':\n \"default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'; font-src 'self'; manifest-src 'self'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'\",\n 'x-content-type-options': 'nosniff',\n 'referrer-policy': 'no-referrer',\n 'x-frame-options': 'DENY',\n 'cache-control': 'no-store',\n};\n\n/**\n * The dashboard's HTML shell. It contains no data; the app behind it requires a session.\n * Assets have fixed names and are cache-busted with the version.\n */\nexport function dashboardShell(): string {\n const v = encodeURIComponent(VERSION);\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<meta name=\"robots\" content=\"noindex\">\n<meta name=\"color-scheme\" content=\"light dark\">\n<title>GSClaw</title>\n<link rel=\"icon\" type=\"image/svg+xml\" href=\"/assets/favicon.svg\">\n<link rel=\"stylesheet\" href=\"/assets/dashboard.css?v=${v}\">\n<script type=\"module\" src=\"/assets/dashboard.js?v=${v}\"></script>\n</head>\n<body>\n<div id=\"app\"><noscript>GSClaw's dashboard needs JavaScript.</noscript></div>\n</body>\n</html>`;\n}\n","// Compact, LLM-friendly formatting. Numbers are rounded, CTR is a percentage, and long result\n// sets are cut to the top N rows with an explicit note about what was left out.\n\nexport interface Metrics {\n clicks: number;\n impressions: number;\n /** Click-through rate in percent (0–100), rounded to 2 decimals. */\n ctr_pct: number;\n /** Average position (1 = top), rounded to 1 decimal. Null when there were no impressions. */\n position: number | null;\n}\n\nexport interface RawMetrics {\n clicks: number;\n impressions: number;\n ctr?: number;\n position?: number;\n}\n\nexport function round(value: number, decimals = 0): number {\n const f = 10 ** decimals;\n return Math.round(value * f) / f;\n}\n\nexport function toMetrics(raw: RawMetrics): Metrics {\n const ctr = raw.ctr ?? (raw.impressions > 0 ? raw.clicks / raw.impressions : 0);\n return {\n clicks: round(raw.clicks),\n impressions: round(raw.impressions),\n ctr_pct: round(ctr * 100, 2),\n position: raw.impressions > 0 && raw.position !== undefined ? round(raw.position, 1) : null,\n };\n}\n\n/** Sums rows into totals, with impression-weighted average position (how GSC aggregates). */\nexport function sumMetrics(rows: RawMetrics[]): Metrics {\n let clicks = 0;\n let impressions = 0;\n let weightedPosition = 0;\n for (const r of rows) {\n clicks += r.clicks;\n impressions += r.impressions;\n weightedPosition += (r.position ?? 0) * r.impressions;\n }\n return toMetrics({\n clicks,\n impressions,\n position: impressions > 0 ? weightedPosition / impressions : undefined,\n });\n}\n\nexport function fmtInt(value: number): string {\n return Math.round(value).toLocaleString('en-US');\n}\n\nexport function fmtPct(pct: number): string {\n const abs = Math.abs(pct);\n return `${abs > 0 && abs < 1 ? pct.toFixed(2) : pct.toFixed(1)}%`;\n}\n\nexport function fmtPos(position: number | null): string {\n return position === null ? '–' : position.toFixed(1);\n}\n\nexport function fmtSigned(value: number, format: (v: number) => string = fmtInt): string {\n if (value === 0) return format(0);\n return value > 0 ? `+${format(value)}` : `-${format(Math.abs(value))}`;\n}\n\n/** Relative change in percent, or null when the base is zero. */\nexport function pctChange(current: number, previous: number): number | null {\n if (previous === 0) return null;\n return round(((current - previous) / previous) * 100, 1);\n}\n\nexport function fmtChange(current: number, previous: number): string {\n const diff = current - previous;\n const rel = pctChange(current, previous);\n if (rel === null) return current === 0 ? '0' : `${fmtSigned(diff)} (new)`;\n return `${fmtSigned(diff)} (${fmtSigned(rel, (v) => `${v.toFixed(1)}%`)})`;\n}\n\nfunction cell(value: string | number | null | undefined): string {\n if (value === null || value === undefined) return '';\n return String(value).replace(/\\|/g, '\\\\|').replace(/\\s+/g, ' ');\n}\n\nexport function mdTable(headers: string[], rows: (string | number | null | undefined)[][]): string {\n if (rows.length === 0) return '_No rows._';\n const head = `| ${headers.join(' | ')} |`;\n const sep = `|${headers.map(() => '---').join('|')}|`;\n const body = rows.map((r) => `| ${r.map(cell).join(' | ')} |`);\n return [head, sep, ...body].join('\\n');\n}\n\nexport function omittedNote(shown: number, total: number, hint: string): string | null {\n if (total <= shown) return null;\n return `_${fmtInt(total - shown)} more row(s) not shown (${fmtInt(shown)} of ${fmtInt(total)}). ${hint}_`;\n}\n\n/**\n * Shortens a page URL for display: same-host URLs become their path. The full URL is always kept\n * in structured output.\n */\nexport function compactUrl(url: string, baseHost?: string): string {\n try {\n const u = new URL(url);\n const path = `${u.pathname}${u.search}${u.hash}`;\n if (baseHost && u.host === baseHost) return path;\n return `${u.host}${path}`;\n } catch {\n return url;\n }\n}\n\n/** Joins non-empty sections with blank lines. */\nexport function sections(...parts: (string | null | undefined | false)[]): string {\n return parts.filter((p): p is string => Boolean(p)).join('\\n\\n');\n}\n\n/** Hard cap so a single tool result never blows past client limits (claude.ai: ~150K chars). */\nexport const MAX_TEXT_CHARS = 60_000;\n\nexport function capText(text: string, max = MAX_TEXT_CHARS): string {\n if (text.length <= max) return text;\n return `${text.slice(0, max)}\\n\\n_[Output truncated at ${fmtInt(max)} characters. Narrow the query or lower \\`limit\\`.]_`;\n}\n","import { z } from 'zod';\n\nexport const seoHealthCheckArgs = z.object({\n site_url: z\n .string()\n .describe(\"Property to review, e.g. 'sc-domain:example.com' or 'example.com'.\"),\n date_range: z\n .enum(['last_28_days', 'last_3_months', 'last_6_months'])\n .optional()\n .describe('Analysis period (default last_28_days).'),\n brand_terms: z\n .string()\n .optional()\n .describe(\n \"Optional RE2 regex of brand queries to exclude from opportunity analysis, e.g. 'acme|acme corp'.\",\n ),\n});\n\nexport function seoHealthCheckPrompt(args: z.output<typeof seoHealthCheckArgs>): string {\n const range = args.date_range ?? 'last_28_days';\n const brand = args.brand_terms\n ? `Pass exclude_queries: \"${args.brand_terms}\" to the opportunity tools so brand searches don't skew results.`\n : 'If the site has obvious brand queries, mention that a brand-excluded view (exclude_queries) may change the picture.';\n return `Run a complete SEO health check of the Search Console property \"${args.site_url}\" using the GSClaw tools. Period: ${range}.\n\nWork through these steps, calling the tools yourself:\n1. list_sites: confirm the exact property and that we have access.\n2. compare_periods (dimension \"none\", then \"query\" and \"page\", compare_to \"previous_period\"): overall trend and the biggest movers. If the period allows, also check compare_to \"year_over_year\" to separate seasonality from real change.\n3. striking_distance_keywords: the best quick-win queries ranking ~8–20.\n4. ctr_opportunities: high-impression queries whose CTR is below expectation for their position (title/description fixes).\n5. keyword_cannibalization: queries split across several pages.\n6. content_decay: pages in steady decline.\n7. list_sitemaps: sitemap errors/warnings and whether Google reads them.\n8. page_report or inspect_url for the 2–3 most important pages you found (top losers or top striking-distance pages), to check index status and canonicals.\n${brand}\n\nThen write a concise report:\n- **Summary**: 3–5 bullets on overall health and trend (with numbers).\n- **Prioritized actions**: a numbered list ordered by expected impact vs effort. For each: the page/query, the evidence (numbers from the tools), and the concrete fix.\n- **Technical issues**: indexing, canonical or sitemap problems, if any.\n- **Watch list**: things to re-check in 2–4 weeks.\n\nRemember: Search Console data lags ~2–3 days, positions are averages, and rare queries are anonymized. Don't invent numbers; cite only what the tools returned.`;\n}\n","import { McpServer } from '@modelcontextprotocol/server';\nimport type { ActivityLog } from './activity.js';\nimport { publicErrorMessage } from './errors.js';\nimport { capText } from './format.js';\nimport type { Logger } from './log.js';\nimport { seoHealthCheckArgs, seoHealthCheckPrompt } from './prompts.js';\nimport type { AnyTool, ToolContext } from './tools/types.js';\nimport { NAME, VERSION } from './version.js';\n\nexport const SERVER_INSTRUCTIONS = `GSClaw gives you read access to Google Search Console (and sitemap writes only if the operator enabled them).\n- Start with list_sites to find the exact property; site_url also accepts a bare domain like \"example.com\".\n- Prefer the analysis tools (compare_periods, striking_distance_keywords, ctr_opportunities, keyword_cannibalization, content_decay, page_report) over raw search_analytics for common questions.\n- Dates are Pacific Time. Final data lags ~2–3 days; relative ranges end on the latest final day. Pass fresh=true for partial recent data.\n- Results show the top rows only and say how many were omitted; raise limit if you need more.\n- Search Console keeps ~16 months of data, and rare queries are anonymized.`;\n\nexport interface McpServerDeps {\n tools: AnyTool[];\n ctx: ToolContext;\n logger: Logger;\n client: string;\n /** Signed-in user's email (OAuth mode), recorded with each activity entry. */\n user?: string;\n activity?: ActivityLog;\n}\n\n/** Builds a fresh MCP server instance (one per HTTP request or stdio connection). */\nexport function buildMcpServer(deps: McpServerDeps): McpServer {\n const server = new McpServer(\n { name: NAME, title: 'GSClaw', version: VERSION },\n { instructions: SERVER_INSTRUCTIONS },\n );\n\n for (const tool of deps.tools) {\n server.registerTool(\n tool.name,\n {\n title: tool.title,\n description: tool.description,\n inputSchema: tool.input,\n annotations: { title: tool.title, ...tool.annotations },\n },\n async (args) => {\n const started = Date.now();\n const site =\n typeof args.site_url === 'string'\n ? args.site_url\n : typeof args.url === 'string'\n ? args.url\n : null;\n try {\n const result = await tool.run(deps.ctx, args);\n const text = capText(tool.format(result, args));\n deps.activity?.record({\n time: new Date().toISOString(),\n client: deps.client,\n tool: tool.name,\n site,\n ms: Date.now() - started,\n ok: true,\n user: deps.user,\n });\n return {\n content: [{ type: 'text', text }],\n structuredContent: result as Record<string, unknown>,\n };\n } catch (error) {\n const { message, kind } = publicErrorMessage(error);\n if (kind === 'internal') deps.logger.error('tool failed', { tool: tool.name, error });\n else deps.logger.info('tool returned error', { tool: tool.name, kind });\n deps.activity?.record({\n time: new Date().toISOString(),\n client: deps.client,\n tool: tool.name,\n site,\n ms: Date.now() - started,\n ok: false,\n error_kind: kind,\n user: deps.user,\n });\n return { content: [{ type: 'text', text: message }], isError: true };\n }\n },\n );\n }\n\n server.registerPrompt(\n 'seo_health_check',\n {\n title: 'SEO health check',\n description:\n 'Full review of a Search Console property: trends, quick wins, CTR fixes, cannibalization, decaying content, sitemaps and indexing, ending in a prioritized action plan.',\n argsSchema: seoHealthCheckArgs,\n },\n (args) => ({\n messages: [{ role: 'user', content: { type: 'text', text: seoHealthCheckPrompt(args) } }],\n }),\n );\n\n return server;\n}\n","interface Entry<V> {\n value: V;\n expiresAt: number;\n weight: number;\n}\n\n/**\n * Small in-memory LRU with TTL and a total weight budget (for example, number of rows held) so a\n * few huge responses cannot exhaust memory on small runtimes such as Cloudflare Workers.\n */\nexport class LruCache<V> {\n private readonly map = new Map<string, Entry<V>>();\n private totalWeight = 0;\n\n constructor(\n private readonly opts: { maxEntries: number; maxWeight: number; now?: () => number },\n ) {}\n\n private now(): number {\n return this.opts.now ? this.opts.now() : Date.now();\n }\n\n get(key: string): V | undefined {\n const entry = this.map.get(key);\n if (!entry) return undefined;\n if (entry.expiresAt <= this.now()) {\n this.delete(key);\n return undefined;\n }\n // Refresh recency.\n this.map.delete(key);\n this.map.set(key, entry);\n return entry.value;\n }\n\n set(key: string, value: V, ttlMs: number, weight = 1): void {\n if (ttlMs <= 0 || weight > this.opts.maxWeight) return;\n this.delete(key);\n this.map.set(key, { value, expiresAt: this.now() + ttlMs, weight });\n this.totalWeight += weight;\n while (this.map.size > this.opts.maxEntries || this.totalWeight > this.opts.maxWeight) {\n const oldest = this.map.keys().next().value;\n if (oldest === undefined) break;\n this.delete(oldest);\n }\n }\n\n delete(key: string): void {\n const entry = this.map.get(key);\n if (!entry) return;\n this.totalWeight -= entry.weight;\n this.map.delete(key);\n }\n\n deleteWhere(predicate: (key: string) => boolean): void {\n for (const key of [...this.map.keys()]) if (predicate(key)) this.delete(key);\n }\n\n get size(): number {\n return this.map.size;\n }\n}\n","import { LruCache } from '../cache.js';\nimport { GscApiError, toGscApiError } from '../errors.js';\nimport type { Logger } from '../log.js';\nimport type { TokenSource } from './auth.js';\nimport type {\n SearchAnalyticsRequest,\n SearchAnalyticsResponse,\n SearchAnalyticsRow,\n SiteEntry,\n Sitemap,\n UrlInspectionResponse,\n} from './types.js';\n\nexport const API_BASE = 'https://searchconsole.googleapis.com';\nconst WEBMASTERS = `${API_BASE}/webmasters/v3`;\nconst INSPECT_URL = `${API_BASE}/v1/urlInspection/index:inspect`;\n\n/** Google's maximum page size for searchanalytics.query. */\nexport const PAGE_SIZE = 25_000;\n\n/** Response cache; keys are scoped by identity, so users never see each other's data. */\nexport type ResponseCache = LruCache<unknown>;\n\n/** Weight budget is in rows, which keeps memory bounded on small runtimes (Workers: 128 MB). */\nexport function createResponseCache(): ResponseCache {\n return new LruCache<unknown>({ maxEntries: 300, maxWeight: 60_000 });\n}\n\nexport interface GscClientOptions {\n tokenSource: TokenSource;\n logger: Logger;\n fetch?: typeof fetch;\n cache?: ResponseCache;\n cacheTtlMs?: number;\n maxRetries?: number;\n timeoutMs?: number;\n sleep?: (ms: number) => Promise<void>;\n}\n\ninterface RequestOptions {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE';\n url: string;\n body?: unknown;\n siteUrl?: string;\n /** Cache weight; omit to skip caching. */\n cacheWeight?: (data: unknown) => number;\n /** Bypass the cached value (the fresh response is still cached). */\n fresh?: boolean;\n}\n\nconst RETRYABLE_STATUS = new Set([429, 500, 502, 503, 504]);\n\nexport class GscClient {\n private readonly tokenSource: TokenSource;\n private readonly logger: Logger;\n private readonly fetchImpl: typeof fetch;\n private readonly cache?: ResponseCache;\n private readonly cacheTtlMs: number;\n private readonly maxRetries: number;\n private readonly timeoutMs: number;\n private readonly sleep: (ms: number) => Promise<void>;\n\n constructor(opts: GscClientOptions) {\n this.tokenSource = opts.tokenSource;\n this.logger = opts.logger;\n this.fetchImpl = opts.fetch ?? ((input, init) => fetch(input, init));\n this.cache = opts.cache;\n this.cacheTtlMs = opts.cacheTtlMs ?? 300_000;\n this.maxRetries = opts.maxRetries ?? 3;\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n this.sleep = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));\n }\n\n get identity() {\n return this.tokenSource.identity;\n }\n\n get canWrite(): boolean {\n return this.tokenSource.scopes.includes('https://www.googleapis.com/auth/webmasters');\n }\n\n /** `fresh` skips the cache read (used by the dashboard's live connectivity check). */\n async listSites(opts: { fresh?: boolean } = {}): Promise<SiteEntry[]> {\n const data = await this.request<{ siteEntry?: SiteEntry[] }>({\n method: 'GET',\n url: `${WEBMASTERS}/sites`,\n cacheWeight: () => 1,\n fresh: opts.fresh,\n });\n return data.siteEntry ?? [];\n }\n\n async searchAnalytics(\n siteUrl: string,\n body: SearchAnalyticsRequest,\n ): Promise<SearchAnalyticsResponse> {\n return this.request<SearchAnalyticsResponse>({\n method: 'POST',\n url: `${WEBMASTERS}/sites/${encodeURIComponent(siteUrl)}/searchAnalytics/query`,\n body,\n siteUrl,\n cacheWeight: (d) => Math.max(1, (d as SearchAnalyticsResponse).rows?.length ?? 0),\n });\n }\n\n /**\n * Fetches up to `maxRows` rows, paging through `startRow` in 25,000-row pages. Google exposes at\n * most ~50K rows per day per search type, so very large sites may still be truncated upstream.\n */\n async searchAnalyticsAll(\n siteUrl: string,\n body: Omit<SearchAnalyticsRequest, 'rowLimit' | 'startRow'>,\n maxRows: number,\n ): Promise<{\n rows: SearchAnalyticsRow[];\n truncated: boolean;\n metadata?: SearchAnalyticsResponse['metadata'];\n }> {\n const rows: SearchAnalyticsRow[] = [];\n let metadata: SearchAnalyticsResponse['metadata'];\n let startRow = 0;\n for (;;) {\n const rowLimit = Math.min(PAGE_SIZE, maxRows - rows.length);\n const res = await this.searchAnalytics(siteUrl, { ...body, rowLimit, startRow });\n metadata ??= res.metadata;\n const page = res.rows ?? [];\n rows.push(...page);\n if (page.length < rowLimit) return { rows, truncated: false, metadata };\n if (rows.length >= maxRows) return { rows, truncated: true, metadata };\n startRow += page.length;\n }\n }\n\n async inspectUrl(\n siteUrl: string,\n inspectionUrl: string,\n languageCode = 'en-US',\n ): Promise<UrlInspectionResponse> {\n return this.request<UrlInspectionResponse>({\n method: 'POST',\n url: INSPECT_URL,\n body: { inspectionUrl, siteUrl, languageCode },\n siteUrl,\n cacheWeight: () => 1,\n });\n }\n\n async listSitemaps(siteUrl: string, sitemapIndex?: string): Promise<Sitemap[]> {\n const query = sitemapIndex ? `?sitemapIndex=${encodeURIComponent(sitemapIndex)}` : '';\n const data = await this.request<{ sitemap?: Sitemap[] }>({\n method: 'GET',\n url: `${WEBMASTERS}/sites/${encodeURIComponent(siteUrl)}/sitemaps${query}`,\n siteUrl,\n cacheWeight: () => 1,\n });\n return data.sitemap ?? [];\n }\n\n async getSitemap(siteUrl: string, feedpath: string): Promise<Sitemap> {\n return this.request<Sitemap>({\n method: 'GET',\n url: `${WEBMASTERS}/sites/${encodeURIComponent(siteUrl)}/sitemaps/${encodeURIComponent(feedpath)}`,\n siteUrl,\n cacheWeight: () => 1,\n });\n }\n\n async submitSitemap(siteUrl: string, feedpath: string): Promise<void> {\n await this.request({\n method: 'PUT',\n url: `${WEBMASTERS}/sites/${encodeURIComponent(siteUrl)}/sitemaps/${encodeURIComponent(feedpath)}`,\n siteUrl,\n });\n this.invalidateSite(siteUrl);\n }\n\n async deleteSitemap(siteUrl: string, feedpath: string): Promise<void> {\n await this.request({\n method: 'DELETE',\n url: `${WEBMASTERS}/sites/${encodeURIComponent(siteUrl)}/sitemaps/${encodeURIComponent(feedpath)}`,\n siteUrl,\n });\n this.invalidateSite(siteUrl);\n }\n\n private cacheKey(opts: RequestOptions): string {\n return `${this.identity.email}\\n${opts.method} ${opts.url}\\n${opts.body ? JSON.stringify(opts.body) : ''}`;\n }\n\n private invalidateSite(siteUrl: string): void {\n const marker = encodeURIComponent(siteUrl);\n this.cache?.deleteWhere(\n (key) => key.startsWith(`${this.identity.email}\\n`) && key.includes(marker),\n );\n }\n\n private async request<T>(opts: RequestOptions): Promise<T> {\n const useCache = this.cache && opts.cacheWeight && this.cacheTtlMs > 0;\n const key = useCache ? this.cacheKey(opts) : '';\n if (useCache && !opts.fresh) {\n const hit = this.cache.get(key);\n if (hit !== undefined) {\n this.logger.debug('gsc cache hit', { method: opts.method, path: pathOf(opts.url) });\n return hit as T;\n }\n }\n\n let refreshedToken = false;\n for (let attempt = 0; ; attempt++) {\n const started = Date.now();\n let res: Response;\n try {\n const token = await this.tokenSource.getAccessToken();\n res = await this.fetchImpl(opts.url, {\n method: opts.method,\n headers: {\n authorization: `Bearer ${token}`,\n ...(opts.body !== undefined ? { 'content-type': 'application/json' } : {}),\n },\n body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n } catch (error) {\n if (error instanceof GscApiError) {\n if (error.retryable && attempt < this.maxRetries) {\n await this.sleep(backoff(attempt));\n continue;\n }\n throw error;\n }\n if (attempt < this.maxRetries) {\n this.logger.warn('gsc network error, retrying', { attempt, error });\n await this.sleep(backoff(attempt));\n continue;\n }\n throw new GscApiError({\n kind: 'network',\n status: 0,\n googleMessage: '',\n retryable: true,\n advice:\n 'Could not reach the Google Search Console API (network error or timeout). Try again shortly.',\n });\n }\n\n this.logger.debug('gsc request', {\n method: opts.method,\n path: pathOf(opts.url),\n status: res.status,\n ms: Date.now() - started,\n });\n\n if (res.ok) {\n const text = await res.text();\n const data = (text ? JSON.parse(text) : {}) as T;\n if (useCache) this.cache.set(key, data, this.cacheTtlMs, opts.cacheWeight!(data));\n return data;\n }\n\n const body: unknown = await res.json().catch(() => undefined);\n if (res.status === 401 && !refreshedToken) {\n refreshedToken = true;\n this.tokenSource.invalidate();\n continue;\n }\n const error = toGscApiError(res.status, body, {\n identity: this.identity,\n siteUrl: opts.siteUrl,\n });\n const retryable =\n RETRYABLE_STATUS.has(res.status) || (error.kind === 'rate_limited' && error.retryable);\n if (retryable && attempt < this.maxRetries) {\n const retryAfter = Number(res.headers.get('retry-after'));\n const delay =\n Number.isFinite(retryAfter) && retryAfter > 0\n ? Math.min(retryAfter * 1000, 10_000)\n : backoff(attempt);\n this.logger.info('gsc retrying', { status: res.status, attempt, delay });\n await this.sleep(delay);\n continue;\n }\n throw error;\n }\n }\n}\n\n/** Exponential backoff with jitter: ~0.5s, 1s, 2s, 4s (+ up to 250ms). */\nexport function backoff(attempt: number): number {\n return 500 * 2 ** attempt + Math.floor(Math.random() * 250);\n}\n\n/** Path for logs, with the (possibly sensitive) site URL segment left encoded. */\nfunction pathOf(url: string): string {\n return url.replace(API_BASE, '');\n}\n","import { pctChange, round, toMetrics, type Metrics, type RawMetrics } from '../format.js';\n\nexport interface MetricChange {\n clicks: number;\n clicks_pct: number | null;\n impressions: number;\n impressions_pct: number | null;\n /** CTR change in percentage points. */\n ctr_pp: number;\n /** Positive = ranks higher than before (average position number went down). */\n rank_improvement: number | null;\n}\n\nexport interface ComparedRow {\n key: string;\n current: Metrics;\n previous: Metrics;\n change: MetricChange;\n status: 'new' | 'lost' | 'both';\n}\n\nconst EMPTY: RawMetrics = { clicks: 0, impressions: 0 };\n\nexport function metricChange(current: Metrics, previous: Metrics): MetricChange {\n return {\n clicks: current.clicks - previous.clicks,\n clicks_pct: pctChange(current.clicks, previous.clicks),\n impressions: current.impressions - previous.impressions,\n impressions_pct: pctChange(current.impressions, previous.impressions),\n ctr_pp: round(current.ctr_pct - previous.ctr_pct, 2),\n rank_improvement:\n current.position !== null && previous.position !== null\n ? round(previous.position - current.position, 1)\n : null,\n };\n}\n\n/** Full outer join of two keyed row sets with per-key deltas. */\nexport function compareRows(\n current: Map<string, RawMetrics>,\n previous: Map<string, RawMetrics>,\n): ComparedRow[] {\n const keys = new Set([...current.keys(), ...previous.keys()]);\n const rows: ComparedRow[] = [];\n for (const key of keys) {\n const cur = toMetrics(current.get(key) ?? EMPTY);\n const prev = toMetrics(previous.get(key) ?? EMPTY);\n rows.push({\n key,\n current: cur,\n previous: prev,\n change: metricChange(cur, prev),\n status: !previous.has(key) ? 'new' : !current.has(key) ? 'lost' : 'both',\n });\n }\n return rows;\n}\n\nexport function topGainers(rows: ComparedRow[], n: number): ComparedRow[] {\n return rows\n .filter((r) => r.change.clicks > 0)\n .sort(\n (a, b) => b.change.clicks - a.change.clicks || b.change.impressions - a.change.impressions,\n )\n .slice(0, n);\n}\n\nexport function topLosers(rows: ComparedRow[], n: number): ComparedRow[] {\n return rows\n .filter((r) => r.change.clicks < 0)\n .sort(\n (a, b) => a.change.clicks - b.change.clicks || a.change.impressions - b.change.impressions,\n )\n .slice(0, n);\n}\n\nexport function topByCurrentClicks(rows: ComparedRow[], n: number): ComparedRow[] {\n return [...rows]\n .filter((r) => r.status !== 'lost')\n .sort(\n (a, b) =>\n b.current.clicks - a.current.clicks || b.current.impressions - a.current.impressions,\n )\n .slice(0, n);\n}\n","// Search Console reports dates in Pacific Time, so every relative preset is resolved in PT.\n// All dates are plain YYYY-MM-DD strings; arithmetic is done in UTC to avoid DST surprises.\n\nexport const DATE_PRESETS = [\n 'last_7_days',\n 'last_28_days',\n 'last_3_months',\n 'last_6_months',\n 'last_12_months',\n 'last_16_months',\n 'previous_period',\n] as const;\nexport type DatePreset = (typeof DATE_PRESETS)[number];\n\nexport const COMPARE_MODES = ['previous_period', 'year_over_year'] as const;\nexport type CompareMode = (typeof COMPARE_MODES)[number];\n\nexport interface DateRange {\n startDate: string;\n endDate: string;\n}\n\n/** Search Console keeps roughly 16 months of performance data. */\nexport const RETENTION_MONTHS = 16;\n/** Final (non-fresh) data typically lags by 2–3 days. */\nexport const DEFAULT_LAG_DAYS = 3;\n\nconst DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nexport function isValidDate(value: string): boolean {\n if (!DATE_RE.test(value)) return false;\n const d = new Date(`${value}T00:00:00Z`);\n return !Number.isNaN(d.getTime()) && d.toISOString().slice(0, 10) === value;\n}\n\nconst ptFormatter = new Intl.DateTimeFormat('en-CA', {\n timeZone: 'America/Los_Angeles',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n});\n\nexport function todayPT(now: Date = new Date()): string {\n return ptFormatter.format(now);\n}\n\nfunction toUtc(date: string): Date {\n return new Date(`${date}T00:00:00Z`);\n}\n\nfunction fromUtc(d: Date): string {\n return d.toISOString().slice(0, 10);\n}\n\nexport function addDays(date: string, days: number): string {\n const d = toUtc(date);\n d.setUTCDate(d.getUTCDate() + days);\n return fromUtc(d);\n}\n\n/** Calendar month arithmetic that clamps to the end of shorter months (Mar 31 − 1 month = Feb 28). */\nexport function addMonths(date: string, months: number): string {\n const d = toUtc(date);\n const day = d.getUTCDate();\n d.setUTCDate(1);\n d.setUTCMonth(d.getUTCMonth() + months);\n const lastDay = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + 1, 0)).getUTCDate();\n d.setUTCDate(Math.min(day, lastDay));\n return fromUtc(d);\n}\n\n/** Inclusive number of days in a range. */\nexport function rangeLength(range: DateRange): number {\n return (\n Math.round((toUtc(range.endDate).getTime() - toUtc(range.startDate).getTime()) / 86_400_000) + 1\n );\n}\n\nexport function resolvePreset(preset: DatePreset, endDate: string): DateRange {\n const lastDays = (n: number): DateRange => ({ startDate: addDays(endDate, -(n - 1)), endDate });\n const lastMonths = (n: number): DateRange => ({\n startDate: addDays(addMonths(endDate, -n), 1),\n endDate,\n });\n switch (preset) {\n case 'last_7_days':\n return lastDays(7);\n case 'last_28_days':\n return lastDays(28);\n case 'last_3_months':\n return lastMonths(3);\n case 'last_6_months':\n return lastMonths(6);\n case 'last_12_months':\n return lastMonths(12);\n case 'last_16_months':\n return lastMonths(RETENTION_MONTHS);\n case 'previous_period':\n return previousPeriod(lastDays(28));\n }\n}\n\n/** The range of equal length immediately before `range`. */\nexport function previousPeriod(range: DateRange): DateRange {\n const length = rangeLength(range);\n const endDate = addDays(range.startDate, -1);\n return { startDate: addDays(endDate, -(length - 1)), endDate };\n}\n\n/** The same calendar dates one year earlier. */\nexport function yearOverYear(range: DateRange): DateRange {\n return { startDate: addMonths(range.startDate, -12), endDate: addMonths(range.endDate, -12) };\n}\n\nexport function comparisonRange(range: DateRange, mode: CompareMode): DateRange {\n return mode === 'year_over_year' ? yearOverYear(range) : previousPeriod(range);\n}\n\n/** Oldest date Search Console still has data for, given today's date (PT). */\nexport function earliestAvailableDate(today: string): string {\n return addDays(addMonths(today, -RETENTION_MONTHS), 1);\n}\n\n/** Splits a range into `count` consecutive, equal-length windows ending at `endDate`. */\nexport function consecutiveWindows(endDate: string, count: number, days: number): DateRange[] {\n const windows: DateRange[] = [];\n let end = endDate;\n for (let i = 0; i < count; i++) {\n const start = addDays(end, -(days - 1));\n windows.unshift({ startDate: start, endDate: end });\n end = addDays(start, -1);\n }\n return windows;\n}\n","// Raw Google Search Console API shapes (webmasters/v3 + searchconsole/v1).\n\nexport const SEARCH_TYPES = ['web', 'image', 'video', 'news', 'discover', 'googleNews'] as const;\nexport type SearchType = (typeof SEARCH_TYPES)[number];\n\nexport const DIMENSIONS = [\n 'query',\n 'page',\n 'country',\n 'device',\n 'date',\n 'searchAppearance',\n 'hour',\n] as const;\nexport type Dimension = (typeof DIMENSIONS)[number];\n\nexport const FILTER_DIMENSIONS = [\n 'query',\n 'page',\n 'country',\n 'device',\n 'searchAppearance',\n] as const;\nexport type FilterDimension = (typeof FILTER_DIMENSIONS)[number];\n\nexport const FILTER_OPERATORS = [\n 'equals',\n 'notEquals',\n 'contains',\n 'notContains',\n 'includingRegex',\n 'excludingRegex',\n] as const;\nexport type FilterOperator = (typeof FILTER_OPERATORS)[number];\n\nexport const AGGREGATION_TYPES = ['auto', 'byPage', 'byProperty', 'byNewsShowcasePanel'] as const;\nexport type AggregationType = (typeof AGGREGATION_TYPES)[number];\n\nexport const DATA_STATES = ['final', 'all', 'hourly_all'] as const;\nexport type DataState = (typeof DATA_STATES)[number];\n\nexport interface SiteEntry {\n siteUrl: string;\n permissionLevel: string;\n}\n\nexport interface DimensionFilter {\n dimension: FilterDimension;\n operator?: FilterOperator;\n expression: string;\n}\n\nexport interface SearchAnalyticsRequest {\n startDate: string;\n endDate: string;\n dimensions?: Dimension[];\n type?: SearchType;\n dimensionFilterGroups?: { groupType?: 'and'; filters: DimensionFilter[] }[];\n aggregationType?: AggregationType;\n rowLimit?: number;\n startRow?: number;\n dataState?: DataState;\n}\n\nexport interface SearchAnalyticsRow {\n keys?: string[];\n clicks: number;\n impressions: number;\n ctr: number;\n position: number;\n}\n\nexport interface SearchAnalyticsResponse {\n rows?: SearchAnalyticsRow[];\n responseAggregationType?: string;\n metadata?: {\n first_incomplete_date?: string;\n first_incomplete_hour?: string;\n firstIncompleteDate?: string;\n firstIncompleteHour?: string;\n };\n}\n\nexport interface InspectionIssue {\n issueMessage?: string;\n message?: string;\n issueType?: string;\n severity?: string;\n}\n\nexport interface UrlInspectionResult {\n inspectionResultLink?: string;\n indexStatusResult?: {\n verdict?: string;\n coverageState?: string;\n robotsTxtState?: string;\n indexingState?: string;\n lastCrawlTime?: string;\n pageFetchState?: string;\n googleCanonical?: string;\n userCanonical?: string;\n sitemap?: string[];\n referringUrls?: string[];\n crawledAs?: string;\n };\n mobileUsabilityResult?: { verdict?: string; issues?: InspectionIssue[] };\n richResultsResult?: {\n verdict?: string;\n detectedItems?: {\n richResultType?: string;\n items?: { name?: string; issues?: InspectionIssue[] }[];\n }[];\n };\n ampResult?: {\n verdict?: string;\n ampUrl?: string;\n indexingState?: string;\n ampIndexStatusVerdict?: string;\n issues?: InspectionIssue[];\n };\n}\n\nexport interface UrlInspectionResponse {\n inspectionResult?: UrlInspectionResult;\n}\n\nexport interface Sitemap {\n path: string;\n lastSubmitted?: string;\n isPending?: boolean;\n isSitemapsIndex?: boolean;\n type?: string;\n lastDownloaded?: string;\n /** int64 fields arrive as strings. */\n warnings?: string | number;\n errors?: string | number;\n contents?: { type?: string; submitted?: string | number; indexed?: string | number }[];\n}\n\nexport interface GoogleErrorBody {\n error?: {\n code?: number;\n message?: string;\n status?: string;\n errors?: { reason?: string; message?: string; domain?: string }[];\n details?: { '@type'?: string; reason?: string; metadata?: Record<string, string> }[];\n };\n}\n","import { z } from 'zod';\nimport {\n addDays,\n DATE_PRESETS,\n DEFAULT_LAG_DAYS,\n earliestAvailableDate,\n isValidDate,\n resolvePreset,\n todayPT,\n type DatePreset,\n type DateRange,\n} from '../dates.js';\nimport { UserFacingError } from '../errors.js';\nimport type { RawMetrics } from '../format.js';\nimport {\n FILTER_DIMENSIONS,\n FILTER_OPERATORS,\n SEARCH_TYPES,\n type Dimension,\n type DimensionFilter,\n type SearchAnalyticsRow,\n type SearchType,\n} from '../gsc/types.js';\nimport { resolveProperty, type Property } from '../gsc/sites.js';\nimport type { ToolContext } from './types.js';\n\nexport const siteUrlField = z\n .string()\n .min(1)\n .describe(\n \"Search Console property: 'sc-domain:example.com', 'https://www.example.com/', or just 'example.com' (matched against your properties; see list_sites).\",\n );\n\nconst dateField = z.string().refine(isValidDate, 'Use YYYY-MM-DD.').optional();\n\nexport const dateRangeFields = {\n date_range: z\n .enum(DATE_PRESETS)\n .default('last_28_days')\n .describe(\n 'Relative period ending on the latest day with final data (Search Console lags ~2–3 days). previous_period = the 28 days before last_28_days.',\n ),\n start_date: dateField.describe(\n 'Explicit start date YYYY-MM-DD (Pacific Time). Use together with end_date; overrides date_range.',\n ),\n end_date: dateField.describe('Explicit end date YYYY-MM-DD (Pacific Time), inclusive.'),\n fresh: z\n .boolean()\n .default(false)\n .describe(\n 'Include fresh, still-changing data from the most recent ~2 days (dataState \"all\"). Default: final data only.',\n ),\n};\n\nexport const searchTypeField = z\n .enum(SEARCH_TYPES)\n .default('web')\n .describe('Search type. Discover and Google News have no query or position data.');\n\nexport const filterSchema = z.object({\n dimension: z.enum(FILTER_DIMENSIONS),\n operator: z\n .enum(FILTER_OPERATORS)\n .default('equals')\n .describe(\n 'equals/notEquals are case-sensitive; contains/notContains are not; regex uses RE2 syntax.',\n ),\n expression: z\n .string()\n .min(1)\n .max(4096)\n .describe(\n 'Value to match. Countries are ISO 3166-1 alpha-3 (e.g. USA); devices are DESKTOP, MOBILE, TABLET.',\n ),\n});\n\nexport const filtersField = z\n .array(filterSchema)\n .max(20)\n .optional()\n .describe('Filters, all ANDed together.');\n\nexport function limitField(defaultValue: number, max = 500) {\n return z\n .number()\n .int()\n .min(1)\n .max(max)\n .default(defaultValue)\n .describe(\n `How many rows to show (top N, default ${defaultValue}). Results always say how many more exist.`,\n );\n}\n\nexport interface DateInput {\n date_range: DatePreset;\n start_date?: string;\n end_date?: string;\n fresh: boolean;\n}\n\nexport interface ResolvedWindow extends DateRange {\n dataState: 'final' | 'all';\n label: string;\n}\n\n/**\n * Latest date that already has final data, found with a cheap date-grouped query (cached by the\n * client). Falls back to today − 3 days for properties with no recent data.\n */\nexport async function latestFinalDate(\n ctx: ToolContext,\n siteUrl: string,\n searchType: SearchType,\n): Promise<string> {\n const today = todayPT(ctx.now());\n const res = await ctx.gsc.searchAnalytics(siteUrl, {\n startDate: addDays(today, -10),\n endDate: today,\n dimensions: ['date'],\n type: searchType,\n dataState: 'final',\n rowLimit: 15,\n });\n const dates = (res.rows ?? []).map((r) => r.keys?.[0]).filter((d): d is string => Boolean(d));\n return dates.length > 0 ? dates.sort().at(-1)! : addDays(today, -DEFAULT_LAG_DAYS);\n}\n\nexport async function resolveWindow(\n ctx: ToolContext,\n property: Property,\n input: DateInput,\n searchType: SearchType = 'web',\n): Promise<ResolvedWindow> {\n const today = todayPT(ctx.now());\n const dataState = input.fresh ? 'all' : 'final';\n\n if (input.start_date || input.end_date) {\n if (!input.start_date || !input.end_date) {\n throw new UserFacingError(\n 'Provide both start_date and end_date, or neither (to use date_range).',\n );\n }\n if (input.start_date > input.end_date)\n throw new UserFacingError('start_date must be on or before end_date.');\n if (input.end_date > today)\n throw new UserFacingError(\n `end_date cannot be in the future (today is ${today} Pacific Time).`,\n );\n const earliest = earliestAvailableDate(today);\n if (input.end_date < earliest) {\n throw new UserFacingError(\n `Search Console only keeps ~16 months of data; the earliest available date is about ${earliest}.`,\n );\n }\n return {\n startDate: input.start_date < earliest ? earliest : input.start_date,\n endDate: input.end_date,\n dataState,\n label: `${input.start_date} → ${input.end_date}`,\n };\n }\n\n const endDate = input.fresh ? today : await latestFinalDate(ctx, property.site_url, searchType);\n const range = resolvePreset(input.date_range, endDate);\n const earliest = earliestAvailableDate(today);\n return {\n ...range,\n startDate: range.startDate < earliest ? earliest : range.startDate,\n dataState,\n label: input.date_range,\n };\n}\n\nexport function windowNote(window: ResolvedWindow): string {\n const state =\n window.dataState === 'all'\n ? 'includes fresh data; the last ~2 days are incomplete and will still change'\n : 'final data only; Search Console lags ~2–3 days';\n return `_${window.startDate} → ${window.endDate} (Pacific Time, ${state})._`;\n}\n\nexport function toFilterGroups(filters: DimensionFilter[] | undefined) {\n if (!filters || filters.length === 0) return undefined;\n return [{ groupType: 'and' as const, filters }];\n}\n\nexport interface FetchOptions {\n type?: SearchType;\n filters?: DimensionFilter[];\n maxRows?: number;\n dataState?: 'final' | 'all';\n}\n\n/** All rows for a window and dimension set (auto-paginated, capped by GSCLAW_MAX_ROWS). */\nexport async function fetchRows(\n ctx: ToolContext,\n siteUrl: string,\n range: DateRange,\n dimensions: Dimension[],\n opts: FetchOptions = {},\n): Promise<{ rows: SearchAnalyticsRow[]; truncated: boolean }> {\n return ctx.gsc.searchAnalyticsAll(\n siteUrl,\n {\n startDate: range.startDate,\n endDate: range.endDate,\n dimensions,\n type: opts.type ?? 'web',\n dimensionFilterGroups: toFilterGroups(opts.filters),\n dataState: opts.dataState ?? 'final',\n },\n Math.min(opts.maxRows ?? ctx.config.maxRows, ctx.config.maxRows),\n );\n}\n\n/** Property totals for a window (a single dimensionless row). */\nexport async function fetchTotals(\n ctx: ToolContext,\n siteUrl: string,\n range: DateRange,\n opts: FetchOptions = {},\n): Promise<RawMetrics> {\n const res = await ctx.gsc.searchAnalytics(siteUrl, {\n startDate: range.startDate,\n endDate: range.endDate,\n type: opts.type ?? 'web',\n dimensionFilterGroups: toFilterGroups(opts.filters),\n dataState: opts.dataState ?? 'final',\n rowLimit: 1,\n });\n return res.rows?.[0] ?? { clicks: 0, impressions: 0 };\n}\n\n/** Keyed rows for one dimension, e.g. query → metrics. */\nexport async function fetchKeyed(\n ctx: ToolContext,\n siteUrl: string,\n range: DateRange,\n dimension: Dimension,\n opts: FetchOptions = {},\n): Promise<{ rows: Map<string, RawMetrics>; truncated: boolean }> {\n const { rows, truncated } = await fetchRows(ctx, siteUrl, range, [dimension], opts);\n return { rows: new Map(rows.map((r) => [r.keys?.[0] ?? '', r])), truncated };\n}\n\n/** Adds an excludingRegex query filter when a pattern is given (e.g. to drop branded queries). */\nexport function withQueryExclusion(\n filters: DimensionFilter[] | undefined,\n pattern: string | undefined,\n): DimensionFilter[] | undefined {\n if (!pattern) return filters;\n return [\n ...(filters ?? []),\n { dimension: 'query', operator: 'excludingRegex', expression: pattern },\n ];\n}\n\nexport const excludeQueriesField = z\n .string()\n .max(4096)\n .optional()\n .describe(\"RE2 regex of queries to exclude, e.g. your brand: 'acme|acme corp'.\");\n\nexport async function propertyFor(ctx: ToolContext, siteUrl: string): Promise<Property> {\n return resolveProperty(ctx.gsc, siteUrl);\n}\n\n/** Host used to shorten page URLs in text output (URL-prefix properties only). */\nexport function displayHost(siteUrl: string): string | undefined {\n if (siteUrl.startsWith('sc-domain:')) return undefined;\n try {\n return new URL(siteUrl).host;\n } catch {\n return undefined;\n }\n}\n\n/** Strips the #fragment Google sometimes reports for jump links and sitelinks. */\nexport function stripFragment(url: string): string {\n const i = url.indexOf('#');\n return i === -1 ? url : url.slice(0, i);\n}\n","import type { z } from 'zod';\nimport type { Config } from '../config.js';\nimport type { GscClient } from '../gsc/client.js';\nimport type { Logger } from '../log.js';\n\nexport interface ToolContext {\n gsc: GscClient;\n config: Pick<Config, 'maxRows' | 'batchInspectMax' | 'allowWrites'>;\n logger: Logger;\n now: () => Date;\n}\n\nexport interface ToolAnnotations {\n readOnlyHint: boolean;\n destructiveHint?: boolean;\n idempotentHint?: boolean;\n openWorldHint: boolean;\n}\n\n/**\n * One tool, defined once. MCP serves `format(run(...))` as text plus the raw result as structured\n * content; the dashboard API calls the very same `run`, so both always see identical numbers.\n */\nexport interface ToolDefinition<S extends z.ZodObject = z.ZodObject, R = unknown> {\n name: string;\n title: string;\n description: string;\n input: S;\n annotations: ToolAnnotations;\n /** Mutating tools are only registered when GSCLAW_ALLOW_WRITES=true and the credentials can write. */\n write?: boolean;\n run(ctx: ToolContext, input: z.output<S>): Promise<R>;\n format(result: R, input: z.output<S>): string;\n}\n\nexport type AnyTool = ToolDefinition<z.ZodObject, unknown>;\n\nexport function defineTool<S extends z.ZodObject, R>(\n def: ToolDefinition<S, R>,\n): ToolDefinition<S, R> {\n return def;\n}\n\nexport const READ_ONLY: ToolAnnotations = {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n};\n","import { z } from 'zod';\nimport {\n compareRows,\n metricChange,\n topByCurrentClicks,\n topGainers,\n topLosers,\n type ComparedRow,\n type MetricChange,\n} from '../analysis/compare.js';\nimport {\n COMPARE_MODES,\n comparisonRange,\n earliestAvailableDate,\n todayPT,\n type CompareMode,\n type DateRange,\n} from '../dates.js';\nimport { UserFacingError } from '../errors.js';\nimport {\n compactUrl,\n fmtChange,\n fmtInt,\n fmtPct,\n fmtPos,\n fmtSigned,\n mdTable,\n sections,\n toMetrics,\n type Metrics,\n} from '../format.js';\nimport {\n dateRangeFields,\n displayHost,\n fetchKeyed,\n fetchTotals,\n filtersField,\n limitField,\n propertyFor,\n resolveWindow,\n searchTypeField,\n siteUrlField,\n} from './common.js';\nimport { defineTool, READ_ONLY } from './types.js';\n\nexport const COMPARE_DIMENSIONS = [\n 'query',\n 'page',\n 'country',\n 'device',\n 'searchAppearance',\n 'none',\n] as const;\nexport const COMPARE_VIEWS = ['movers', 'top', 'all'] as const;\n\nexport interface ComparePeriodsResult {\n site_url: string;\n dimension: (typeof COMPARE_DIMENSIONS)[number];\n compare_to: CompareMode;\n current: DateRange;\n previous: DateRange;\n data_state: 'final' | 'all';\n totals: { current: Metrics; previous: Metrics; change: MetricChange };\n /** Top rows by current-period clicks, with deltas (view = top | all). */\n top: ComparedRow[];\n gainers: ComparedRow[];\n losers: ComparedRow[];\n new_count: number;\n lost_count: number;\n compared_count: number;\n truncated: boolean;\n}\n\nexport const comparePeriods = defineTool({\n name: 'compare_periods',\n title: 'Compare periods (gainers & losers)',\n description:\n 'Compares a period with the previous period or the same dates last year: property totals with deltas, plus the top gaining and losing queries/pages/countries/devices by clicks. ' +\n 'Use for \"what changed?\", traffic drops, and month-over-month or year-over-year reports. dimension \"none\" returns totals only.',\n input: z.object({\n site_url: siteUrlField,\n ...dateRangeFields,\n compare_to: z\n .enum(COMPARE_MODES)\n .default('previous_period')\n .describe(\n 'previous_period (same length, right before) or year_over_year (same dates last year).',\n ),\n dimension: z.enum(COMPARE_DIMENSIONS).default('query'),\n view: z\n .enum(COMPARE_VIEWS)\n .default('movers')\n .describe('movers = top gainers + losers; top = biggest rows now with deltas; all = both.'),\n filters: filtersField,\n search_type: searchTypeField,\n limit: limitField(10, 100),\n }),\n annotations: READ_ONLY,\n async run(ctx, input): Promise<ComparePeriodsResult> {\n const property = await propertyFor(ctx, input.site_url);\n const current = await resolveWindow(ctx, property, input, input.search_type);\n const previous = comparisonRange(current, input.compare_to);\n const earliest = earliestAvailableDate(todayPT(ctx.now()));\n if (previous.startDate < earliest) {\n throw new UserFacingError(\n `The comparison period would start ${previous.startDate}, but Search Console only keeps ~16 months of data (earliest ≈ ${earliest}). Use a shorter date_range or compare_to \"previous_period\".`,\n );\n }\n const opts = { type: input.search_type, filters: input.filters, dataState: current.dataState };\n const dimension = input.dimension === 'none' ? null : input.dimension;\n const [curTotals, prevTotals, curRows, prevRows] = await Promise.all([\n fetchTotals(ctx, property.site_url, current, opts),\n fetchTotals(ctx, property.site_url, previous, opts),\n dimension ? fetchKeyed(ctx, property.site_url, current, dimension, opts) : null,\n dimension ? fetchKeyed(ctx, property.site_url, previous, dimension, opts) : null,\n ]);\n const rows = curRows && prevRows ? compareRows(curRows.rows, prevRows.rows) : [];\n const cur = toMetrics(curTotals);\n const prev = toMetrics(prevTotals);\n const wantTop = input.view !== 'movers';\n const wantMovers = input.view !== 'top';\n return {\n site_url: property.site_url,\n dimension: input.dimension,\n compare_to: input.compare_to,\n current: { startDate: current.startDate, endDate: current.endDate },\n previous,\n data_state: current.dataState,\n totals: { current: cur, previous: prev, change: metricChange(cur, prev) },\n top: wantTop ? topByCurrentClicks(rows, input.limit) : [],\n gainers: wantMovers ? topGainers(rows, input.limit) : [],\n losers: wantMovers ? topLosers(rows, input.limit) : [],\n new_count: rows.filter((r) => r.status === 'new').length,\n lost_count: rows.filter((r) => r.status === 'lost').length,\n compared_count: rows.length,\n truncated: Boolean(curRows?.truncated || prevRows?.truncated),\n };\n },\n format(r) {\n const t = r.totals;\n const host = displayHost(r.site_url);\n const label = (key: string) => (r.dimension === 'page' ? compactUrl(key, host) : key);\n const totals = mdTable(\n ['Metric', 'Current', 'Previous', 'Change'],\n [\n [\n 'Clicks',\n fmtInt(t.current.clicks),\n fmtInt(t.previous.clicks),\n fmtChange(t.current.clicks, t.previous.clicks),\n ],\n [\n 'Impressions',\n fmtInt(t.current.impressions),\n fmtInt(t.previous.impressions),\n fmtChange(t.current.impressions, t.previous.impressions),\n ],\n [\n 'CTR',\n fmtPct(t.current.ctr_pct),\n fmtPct(t.previous.ctr_pct),\n `${fmtSigned(t.change.ctr_pp, (v) => v.toFixed(2))} pp`,\n ],\n [\n 'Avg position',\n fmtPos(t.current.position),\n fmtPos(t.previous.position),\n positionChange(t.change.rank_improvement),\n ],\n ],\n );\n const table = (title: string, rows: ComparedRow[]) =>\n rows.length === 0\n ? null\n : sections(\n `**${title}**`,\n mdTable(\n [r.dimension, 'Clicks', 'Δ clicks', 'Impr.', 'Δ impr.', 'Pos.', 'Δ pos.'],\n rows.map((row) => [\n label(row.key),\n fmtInt(row.current.clicks),\n fmtChange(row.current.clicks, row.previous.clicks),\n fmtInt(row.current.impressions),\n fmtSigned(row.change.impressions),\n fmtPos(row.current.position),\n positionChange(row.change.rank_improvement),\n ]),\n ),\n );\n return sections(\n `**${r.site_url}: ${r.current.startDate} → ${r.current.endDate} vs ${r.previous.startDate} → ${r.previous.endDate}** (${r.compare_to === 'year_over_year' ? 'year over year' : 'previous period'}, ${r.data_state} data)`,\n totals,\n table(`Top ${r.dimension} values now`, r.top),\n table('Top gainers (by clicks)', r.gainers),\n table('Top losers (by clicks)', r.losers),\n r.dimension !== 'none'\n ? `_${fmtInt(r.compared_count)} ${r.dimension} values compared: ${fmtInt(r.new_count)} new, ${fmtInt(r.lost_count)} no longer appearing.${r.truncated ? ' Row cap reached; the long tail is not included.' : ''}_`\n : null,\n );\n },\n});\n\nexport function positionChange(improvement: number | null): string {\n if (improvement === null) return '–';\n if (improvement === 0) return '0';\n return improvement > 0 ? `↑${improvement.toFixed(1)}` : `↓${Math.abs(improvement).toFixed(1)}`;\n}\n","import { round, toMetrics, type RawMetrics } from '../format.js';\n\nexport interface DecayRow {\n page: string;\n /** Clicks per window, oldest first. */\n clicks: number[];\n impressions: number[];\n position: (number | null)[];\n baseline_clicks: number;\n latest_clicks: number;\n lost_clicks: number;\n decline_pct: number;\n /** Number of window-to-window declines (sustained decay has many). */\n declines: number;\n likely_cause: 'ranking_loss' | 'lower_demand' | 'lower_ctr' | 'mixed';\n}\n\n/**\n * Pages whose clicks fell steadily across consecutive windows: at least `minBaselineClicks` in the\n * first window, at least `minDeclinePct` lower in the last one, and falling in all but at most one\n * window-to-window step.\n */\nexport function findContentDecay(\n series: Map<string, (RawMetrics | undefined)[]>,\n windows: number,\n opts: { minBaselineClicks: number; minDeclinePct: number },\n): DecayRow[] {\n const out: DecayRow[] = [];\n const requiredDeclines = Math.max(1, windows - 2);\n for (const [page, values] of series) {\n const metrics = Array.from({ length: windows }, (_, i) =>\n toMetrics(values[i] ?? { clicks: 0, impressions: 0 }),\n );\n const clicks = metrics.map((m) => m.clicks);\n const baseline = clicks[0]!;\n const latest = clicks.at(-1)!;\n if (baseline < opts.minBaselineClicks) continue;\n const declinePct = ((baseline - latest) / baseline) * 100;\n if (declinePct < opts.minDeclinePct) continue;\n let declines = 0;\n for (let i = 1; i < clicks.length; i++) if (clicks[i]! < clicks[i - 1]!) declines++;\n if (declines < requiredDeclines) continue;\n\n const first = metrics[0]!;\n const last = metrics.at(-1)!;\n const positionDrop =\n first.position !== null && last.position !== null ? last.position - first.position : 0;\n const impressionDrop = first.impressions\n ? (first.impressions - last.impressions) / first.impressions\n : 0;\n const ctrDrop = first.ctr_pct ? (first.ctr_pct - last.ctr_pct) / first.ctr_pct : 0;\n let cause: DecayRow['likely_cause'] = 'mixed';\n if (positionDrop >= 1.5) cause = 'ranking_loss';\n else if (impressionDrop >= 0.2 && Math.abs(positionDrop) < 1) cause = 'lower_demand';\n else if (ctrDrop >= 0.2 && Math.abs(positionDrop) < 1) cause = 'lower_ctr';\n\n out.push({\n page,\n clicks,\n impressions: metrics.map((m) => m.impressions),\n position: metrics.map((m) => m.position),\n baseline_clicks: baseline,\n latest_clicks: latest,\n lost_clicks: baseline - latest,\n decline_pct: round(declinePct, 1),\n declines,\n likely_cause: cause,\n });\n }\n return out.sort((a, b) => b.lost_clicks - a.lost_clicks);\n}\n","import { z } from 'zod';\nimport { findContentDecay, type DecayRow } from '../analysis/decay.js';\nimport { consecutiveWindows, earliestAvailableDate, todayPT, type DateRange } from '../dates.js';\nimport { UserFacingError } from '../errors.js';\nimport {\n compactUrl,\n fmtInt,\n fmtPos,\n mdTable,\n omittedNote,\n sections,\n type RawMetrics,\n} from '../format.js';\nimport {\n displayHost,\n excludeQueriesField,\n fetchKeyed,\n latestFinalDate,\n limitField,\n propertyFor,\n siteUrlField,\n} from './common.js';\nimport { defineTool, READ_ONLY } from './types.js';\n\nexport interface ContentDecayResult {\n site_url: string;\n windows: DateRange[];\n min_baseline_clicks: number;\n min_decline_pct: number;\n total_matches: number;\n rows: DecayRow[];\n truncated: boolean;\n}\n\nconst CAUSE_LABEL: Record<DecayRow['likely_cause'], string> = {\n ranking_loss: 'rankings dropped',\n lower_demand: 'fewer impressions (demand/seasonality)',\n lower_ctr: 'CTR fell (SERP/snippet change)',\n mixed: 'mixed',\n};\n\nexport const contentDecay = defineTool({\n name: 'content_decay',\n title: 'Content decay',\n description:\n 'Finds pages whose clicks declined steadily over consecutive windows (default 4 × 28 days), not just one bad week. For each page: clicks per window, total decline, position trend and a likely cause (ranking loss, lower demand, lower CTR).',\n input: z.object({\n site_url: siteUrlField,\n windows: z\n .number()\n .int()\n .min(3)\n .max(8)\n .default(4)\n .describe('Number of consecutive windows to compare.'),\n window_days: z\n .number()\n .int()\n .min(7)\n .max(91)\n .default(28)\n .describe('Length of each window in days.'),\n min_baseline_clicks: z\n .number()\n .int()\n .min(1)\n .default(20)\n .describe('Minimum clicks in the oldest window.'),\n min_decline_pct: z\n .number()\n .min(1)\n .max(100)\n .default(25)\n .describe('Minimum drop from the oldest to the newest window, in percent.'),\n exclude_queries: excludeQueriesField,\n limit: limitField(20),\n }),\n annotations: READ_ONLY,\n async run(ctx, input): Promise<ContentDecayResult> {\n const property = await propertyFor(ctx, input.site_url);\n const end = await latestFinalDate(ctx, property.site_url, 'web');\n const windows = consecutiveWindows(end, input.windows, input.window_days);\n const earliest = earliestAvailableDate(todayPT(ctx.now()));\n if (windows[0]!.startDate < earliest) {\n throw new UserFacingError(\n `${input.windows} × ${input.window_days} days reaches back to ${windows[0]!.startDate}, beyond Search Console's ~16 months of data. Use fewer or shorter windows.`,\n );\n }\n const filters = input.exclude_queries\n ? [\n {\n dimension: 'query' as const,\n operator: 'excludingRegex' as const,\n expression: input.exclude_queries,\n },\n ]\n : undefined;\n const results = await Promise.all(\n windows.map((w) => fetchKeyed(ctx, property.site_url, w, 'page', { filters })),\n );\n const series = new Map<string, (RawMetrics | undefined)[]>();\n results.forEach((result, i) => {\n for (const [page, metrics] of result.rows) {\n const url = page.split('#')[0]!;\n const list =\n series.get(url) ?? new Array<RawMetrics | undefined>(windows.length).fill(undefined);\n const prev = list[i];\n list[i] = prev ? mergeMetrics(prev, metrics) : metrics;\n series.set(url, list);\n }\n });\n const matches = findContentDecay(series, windows.length, {\n minBaselineClicks: input.min_baseline_clicks,\n minDeclinePct: input.min_decline_pct,\n });\n return {\n site_url: property.site_url,\n windows,\n min_baseline_clicks: input.min_baseline_clicks,\n min_decline_pct: input.min_decline_pct,\n total_matches: matches.length,\n rows: matches.slice(0, input.limit),\n truncated: results.some((r) => r.truncated),\n };\n },\n format(r) {\n const host = displayHost(r.site_url);\n const span = `${r.windows[0]!.startDate} → ${r.windows.at(-1)!.endDate}, ${r.windows.length} windows`;\n if (r.total_matches === 0) {\n return `No decaying pages on ${r.site_url} (${span}; ≥${r.min_baseline_clicks} clicks in the first window and ≥${r.min_decline_pct}% sustained decline).`;\n }\n return sections(\n `**${fmtInt(r.total_matches)} decaying pages** on ${r.site_url} (${span})`,\n mdTable(\n ['Page', 'Clicks per window', 'Decline', 'Position', 'Likely cause'],\n r.rows.map((row) => [\n compactUrl(row.page, host),\n row.clicks.map(fmtInt).join(' → '),\n `-${fmtInt(row.lost_clicks)} (-${row.decline_pct}%)`,\n `${fmtPos(row.position[0] ?? null)} → ${fmtPos(row.position.at(-1) ?? null)}`,\n CAUSE_LABEL[row.likely_cause],\n ]),\n ),\n omittedNote(r.rows.length, r.total_matches, 'Raise `limit` to see more.'),\n r.truncated ? '_Row cap reached in at least one window; small pages may be missing._' : null,\n );\n },\n});\n\nfunction mergeMetrics(a: RawMetrics, b: RawMetrics): RawMetrics {\n const impressions = a.impressions + b.impressions;\n return {\n clicks: a.clicks + b.clicks,\n impressions,\n position: impressions\n ? ((a.position ?? 0) * a.impressions + (b.position ?? 0) * b.impressions) / impressions\n : undefined,\n };\n}\n","import { z } from 'zod';\nimport { publicErrorMessage, UserFacingError } from '../errors.js';\nimport { compactUrl, mdTable, sections } from '../format.js';\nimport type { GscClient } from '../gsc/client.js';\nimport { listProperties, propertyCoversUrl, resolveProperty, type Property } from '../gsc/sites.js';\nimport type { UrlInspectionResult } from '../gsc/types.js';\nimport { displayHost } from './common.js';\nimport { defineTool, READ_ONLY, type ToolContext } from './types.js';\n\nexport interface InspectionSummary {\n url: string;\n site_url: string;\n verdict: string;\n coverage_state: string | null;\n indexing_state: string | null;\n robots_txt_state: string | null;\n page_fetch_state: string | null;\n last_crawl_time: string | null;\n crawled_as: string | null;\n google_canonical: string | null;\n user_canonical: string | null;\n /** True when Google chose a different canonical than the page declares. */\n canonical_mismatch: boolean;\n sitemaps: string[];\n referring_urls: string[];\n /** Deprecated by Google; reported only when present. */\n mobile_usability: { verdict: string; issues: string[] } | null;\n rich_results: {\n verdict: string;\n items: { type: string; name: string | null; issues: string[] }[];\n } | null;\n inspection_link: string | null;\n}\n\nconst urlField = z\n .string()\n .url()\n .describe('Full URL of the page to inspect (must belong to the property).');\nconst optionalSiteField = z\n .string()\n .optional()\n .describe(\n 'Property that contains the URL. Optional: inferred from your properties when omitted.',\n );\n\n/** Finds the most specific readable property that contains `url`. */\nexport async function propertyForUrl(\n gsc: GscClient,\n url: string,\n siteUrl?: string,\n): Promise<Property> {\n if (siteUrl) {\n const property = await resolveProperty(gsc, siteUrl);\n if (!propertyCoversUrl(property, url)) {\n throw new UserFacingError(`${url} is not inside the property '${property.site_url}'.`);\n }\n return property;\n }\n const properties = (await listProperties(gsc)).filter(\n (p) => p.has_access && propertyCoversUrl(p, url),\n );\n // URL-prefix properties are more specific than domain properties; longer prefixes win.\n properties.sort(\n (a, b) =>\n Number(b.type === 'url_prefix') - Number(a.type === 'url_prefix') ||\n b.site_url.length - a.site_url.length,\n );\n const best = properties[0];\n if (!best)\n throw new UserFacingError(\n `None of your Search Console properties contains ${url}. Run list_sites to check.`,\n 'not_found',\n );\n return best;\n}\n\nexport function summarizeInspection(\n url: string,\n siteUrl: string,\n result: UrlInspectionResult | undefined,\n): InspectionSummary {\n const idx = result?.indexStatusResult ?? {};\n const google = idx.googleCanonical ?? null;\n const user = idx.userCanonical ?? null;\n const issues = (list?: { issueMessage?: string; message?: string; severity?: string }[]) =>\n (list ?? []).map(\n (i) => `${i.severity ? `${i.severity}: ` : ''}${i.issueMessage ?? i.message ?? 'issue'}`,\n );\n return {\n url,\n site_url: siteUrl,\n verdict: idx.verdict ?? 'VERDICT_UNSPECIFIED',\n coverage_state: idx.coverageState ?? null,\n indexing_state: idx.indexingState ?? null,\n robots_txt_state: idx.robotsTxtState ?? null,\n page_fetch_state: idx.pageFetchState ?? null,\n last_crawl_time: idx.lastCrawlTime ?? null,\n crawled_as: idx.crawledAs ?? null,\n google_canonical: google,\n user_canonical: user,\n canonical_mismatch: Boolean(google && user && google !== user),\n sitemaps: idx.sitemap ?? [],\n referring_urls: (idx.referringUrls ?? []).slice(0, 10),\n mobile_usability: result?.mobileUsabilityResult\n ? {\n verdict: result.mobileUsabilityResult.verdict ?? 'VERDICT_UNSPECIFIED',\n issues: issues(result.mobileUsabilityResult.issues),\n }\n : null,\n rich_results: result?.richResultsResult\n ? {\n verdict: result.richResultsResult.verdict ?? 'VERDICT_UNSPECIFIED',\n items: (result.richResultsResult.detectedItems ?? []).flatMap((d) =>\n (d.items ?? [{}]).map((item) => ({\n type: d.richResultType ?? 'unknown',\n name: item.name ?? null,\n issues: issues(item.issues),\n })),\n ),\n }\n : null,\n inspection_link: result?.inspectionResultLink ?? null,\n };\n}\n\nexport async function inspectOne(\n ctx: ToolContext,\n url: string,\n siteUrl?: string,\n languageCode?: string,\n): Promise<InspectionSummary> {\n const property = await propertyForUrl(ctx.gsc, url, siteUrl);\n const res = await ctx.gsc.inspectUrl(property.site_url, url, languageCode);\n return summarizeInspection(url, property.site_url, res.inspectionResult);\n}\n\nexport function formatInspection(s: InspectionSummary): string {\n const lines = [\n `**URL Inspection** for ${s.url}`,\n `- Verdict: **${s.verdict}**${s.coverage_state ? ` — ${s.coverage_state}` : ''}`,\n `- Indexing: ${s.indexing_state ?? 'unknown'} · robots.txt: ${s.robots_txt_state ?? 'unknown'} · fetch: ${s.page_fetch_state ?? 'unknown'}`,\n `- Last crawl: ${s.last_crawl_time ?? 'never / unknown'}${s.crawled_as ? ` (${s.crawled_as.toLowerCase()} crawler)` : ''}`,\n `- Canonical — Google: ${s.google_canonical ?? 'n/a'} · declared: ${s.user_canonical ?? 'n/a'}${s.canonical_mismatch ? ' ⚠️ **mismatch**' : ''}`,\n ];\n if (s.sitemaps.length) lines.push(`- In sitemaps: ${s.sitemaps.join(', ')}`);\n if (s.rich_results) {\n const items = s.rich_results.items\n .map(\n (i) =>\n `${i.type}${i.name ? ` (${i.name})` : ''}${i.issues.length ? `: ${i.issues.join('; ')}` : ''}`,\n )\n .join(' · ');\n lines.push(`- Rich results: ${s.rich_results.verdict}${items ? ` — ${items}` : ''}`);\n }\n if (s.mobile_usability) {\n lines.push(\n `- Mobile usability (deprecated report): ${s.mobile_usability.verdict}${s.mobile_usability.issues.length ? ` — ${s.mobile_usability.issues.join('; ')}` : ''}`,\n );\n }\n if (s.inspection_link) lines.push(`- Open in Search Console: ${s.inspection_link}`);\n lines.push('_URL Inspection reports the indexed version; it is not a live test._');\n return lines.join('\\n');\n}\n\nexport const inspectUrl = defineTool({\n name: 'inspect_url',\n title: 'Inspect URL (index status)',\n description:\n \"Shows Google's index status for one URL: verdict, coverage state, robots.txt and fetch status, last crawl, Google-selected vs declared canonical, sitemaps and rich results. Uses the URL Inspection API (quota: 2,000/day and 600/min per property).\",\n input: z.object({\n url: urlField,\n site_url: optionalSiteField,\n language_code: z.string().default('en-US').describe('Language for issue messages (BCP-47).'),\n }),\n annotations: READ_ONLY,\n async run(ctx, input) {\n return inspectOne(ctx, input.url, input.site_url, input.language_code);\n },\n format: formatInspection,\n});\n\nexport interface BatchInspectionResult {\n inspected: number;\n failed: number;\n verdicts: Record<string, number>;\n coverage: Record<string, number>;\n canonical_mismatches: number;\n results: InspectionSummary[];\n errors: { url: string; error: string }[];\n}\n\nasync function mapWithConcurrency<T, R>(\n items: T[],\n concurrency: number,\n fn: (item: T) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n let next = 0;\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {\n while (next < items.length) {\n const i = next++;\n results[i] = await fn(items[i]!);\n }\n });\n await Promise.all(workers);\n return results;\n}\n\nexport const batchInspectUrls = defineTool({\n name: 'batch_inspect_urls',\n title: 'Inspect many URLs',\n description:\n 'Runs URL Inspection on a list of URLs with limited concurrency and summarizes index status (verdicts, coverage states, canonical mismatches). Each URL costs one URL Inspection call (2,000/day and 600/min per property), so keep batches focused.',\n input: z.object({\n urls: z\n .array(z.string().url())\n .min(1)\n .max(500)\n .describe('URLs to inspect (the server caps the batch size; default cap 50).'),\n site_url: optionalSiteField,\n concurrency: z\n .number()\n .int()\n .min(1)\n .max(10)\n .default(5)\n .describe('Parallel requests (max 10 ≈ the 600/min quota).'),\n }),\n annotations: READ_ONLY,\n async run(ctx, input): Promise<BatchInspectionResult> {\n const urls = [...new Set(input.urls)];\n if (urls.length > ctx.config.batchInspectMax) {\n throw new UserFacingError(\n `Too many URLs (${urls.length}). This server allows up to ${ctx.config.batchInspectMax} per batch (GSCLAW_BATCH_INSPECT_MAX) to protect the 2,000/day URL Inspection quota. Split the list.`,\n );\n }\n const outcomes = await mapWithConcurrency(urls, input.concurrency, async (url) => {\n try {\n return { ok: true as const, value: await inspectOne(ctx, url, input.site_url) };\n } catch (error) {\n return {\n ok: false as const,\n url,\n error: publicErrorMessage(error).message.split('\\n')[0]!,\n };\n }\n });\n const results = outcomes.flatMap((o) => (o.ok ? [o.value] : []));\n const errors = outcomes.flatMap((o) => (o.ok ? [] : [{ url: o.url, error: o.error }]));\n const count = (values: (string | null)[]) =>\n values.reduce<Record<string, number>>((acc, v) => {\n const k = v ?? 'unknown';\n acc[k] = (acc[k] ?? 0) + 1;\n return acc;\n }, {});\n return {\n inspected: results.length,\n failed: errors.length,\n verdicts: count(results.map((r) => r.verdict)),\n coverage: count(results.map((r) => r.coverage_state)),\n canonical_mismatches: results.filter((r) => r.canonical_mismatch).length,\n results,\n errors,\n };\n },\n format(r) {\n const host = r.results[0] ? displayHost(r.results[0].site_url) : undefined;\n const summary = Object.entries(r.coverage)\n .sort((a, b) => b[1] - a[1])\n .map(([state, n]) => `${n} × ${state}`)\n .join(' · ');\n const table = mdTable(\n ['URL', 'Verdict', 'Coverage', 'Last crawl', 'Canonical'],\n r.results.map((s) => [\n compactUrl(s.url, host),\n s.verdict,\n s.coverage_state,\n s.last_crawl_time?.slice(0, 10) ?? '—',\n s.canonical_mismatch\n ? `⚠️ Google chose ${compactUrl(s.google_canonical ?? '', host)}`\n : 'ok',\n ]),\n );\n return sections(\n `**Inspected ${r.inspected} URL(s)**${r.failed ? `, ${r.failed} failed` : ''}. ${summary}`,\n r.canonical_mismatches\n ? `⚠️ ${r.canonical_mismatches} URL(s) where Google picked a different canonical.`\n : null,\n table,\n r.errors.length\n ? `**Errors:**\\n${r.errors.map((e) => `- ${e.url}: ${e.error}`).join('\\n')}`\n : null,\n );\n },\n});\n","import { round } from '../format.js';\n\nexport interface CurvePoint {\n position: number;\n /** Expected CTR in percent. */\n ctr_pct: number;\n source: 'site' | 'industry';\n samples: number;\n}\n\n/**\n * Approximate organic CTR by position (percent), compiled from public click-through-rate studies\n * of Google results. Real CTRs vary a lot by query intent, brand vs non-brand, device and SERP\n * features such as AI Overviews, which is why GSClaw prefers the site's own curve when it has\n * enough data.\n */\nexport const INDUSTRY_CTR_CURVE: readonly number[] = [\n 27.0,\n 15.0,\n 11.0,\n 8.0,\n 6.0,\n 4.6,\n 3.6,\n 2.9,\n 2.4,\n 2.0, // positions 1–10\n 1.6,\n 1.4,\n 1.2,\n 1.1,\n 1.0,\n 0.9,\n 0.8,\n 0.7,\n 0.65,\n 0.6, // positions 11–20\n];\n\nexport const MAX_CURVE_POSITION = INDUSTRY_CTR_CURVE.length;\n\nexport interface CurveInputRow {\n clicks: number;\n impressions: number;\n position: number;\n}\n\nfunction median(values: number[]): number {\n const sorted = [...values].sort((a, b) => a - b);\n const mid = Math.floor(sorted.length / 2);\n return sorted.length % 2 ? sorted[mid]! : (sorted[mid - 1]! + sorted[mid]!) / 2;\n}\n\n/**\n * Builds a CTR-by-position curve. With `benchmark: 'site'`, each position bucket uses the median\n * CTR of the site's own rows (rows with at least `minImpressions`) when it has `minSamples` of\n * them, and the industry curve otherwise. The curve is forced to be non-increasing.\n */\nexport function buildCtrCurve(\n rows: CurveInputRow[],\n opts: { benchmark: 'site' | 'industry'; minImpressions?: number; minSamples?: number },\n): CurvePoint[] {\n const minImpressions = opts.minImpressions ?? 20;\n const minSamples = opts.minSamples ?? 8;\n const buckets = new Map<number, number[]>();\n if (opts.benchmark === 'site') {\n for (const r of rows) {\n if (r.impressions < minImpressions) continue;\n const bucket = Math.round(r.position);\n if (bucket < 1 || bucket > MAX_CURVE_POSITION) continue;\n const list = buckets.get(bucket) ?? [];\n list.push((r.clicks / r.impressions) * 100);\n buckets.set(bucket, list);\n }\n }\n\n const curve: CurvePoint[] = [];\n for (let position = 1; position <= MAX_CURVE_POSITION; position++) {\n const samples = buckets.get(position) ?? [];\n const useSite = samples.length >= minSamples;\n let ctr = useSite ? median(samples) : INDUSTRY_CTR_CURVE[position - 1]!;\n const prev = curve.at(-1);\n if (prev && ctr > prev.ctr_pct) ctr = prev.ctr_pct;\n curve.push({\n position,\n ctr_pct: round(ctr, 2),\n source: useSite ? 'site' : 'industry',\n samples: samples.length,\n });\n }\n return curve;\n}\n\n/** Expected CTR (percent) at a fractional position, interpolating linearly between buckets. */\nexport function expectedCtr(curve: CurvePoint[], position: number): number {\n if (curve.length === 0) return 0;\n if (position <= 1) return curve[0]!.ctr_pct;\n if (position >= curve.length) return curve.at(-1)!.ctr_pct;\n const lower = Math.floor(position);\n const frac = position - lower;\n const a = curve[lower - 1]!.ctr_pct;\n const b = curve[lower]!.ctr_pct;\n return a + (b - a) * frac;\n}\n","import { round, toMetrics, type Metrics } from '../format.js';\nimport { expectedCtr, type CurvePoint } from './ctr-curve.js';\n\n/** A query × page row as returned by Search Console. */\nexport interface QueryPageRow {\n query: string;\n page: string;\n clicks: number;\n impressions: number;\n ctr: number;\n position: number;\n}\n\ninterface PageShare extends Metrics {\n page: string;\n share_pct: number;\n}\n\nfunction groupByQuery(\n rows: QueryPageRow[],\n mergeFragments: boolean,\n): Map<string, Map<string, QueryPageRow>> {\n const byQuery = new Map<string, Map<string, QueryPageRow>>();\n for (const row of rows) {\n const page = mergeFragments ? row.page.split('#')[0]! : row.page;\n const pages = byQuery.get(row.query) ?? new Map<string, QueryPageRow>();\n const existing = pages.get(page);\n if (existing) {\n const impressions = existing.impressions + row.impressions;\n pages.set(page, {\n ...existing,\n clicks: existing.clicks + row.clicks,\n impressions,\n ctr: impressions ? (existing.clicks + row.clicks) / impressions : 0,\n position: impressions\n ? (existing.position * existing.impressions + row.position * row.impressions) /\n impressions\n : existing.position,\n });\n } else {\n pages.set(page, { ...row, page });\n }\n byQuery.set(row.query, pages);\n }\n return byQuery;\n}\n\n// ---------------------------------------------------------------------------------------------\n// Striking distance\n// ---------------------------------------------------------------------------------------------\n\nexport interface StrikingDistanceRow extends Metrics {\n query: string;\n /** The page that ranks for the query (most impressions). */\n page: string;\n /** Other pages of the site that also appear for this query. */\n other_pages: number;\n /** Estimated extra clicks/period if this page reached `target_position`. */\n potential_extra_clicks: number;\n}\n\nexport function findStrikingDistance(\n rows: QueryPageRow[],\n opts: {\n minPosition: number;\n maxPosition: number;\n minImpressions: number;\n targetPosition: number;\n curve: CurvePoint[];\n },\n): StrikingDistanceRow[] {\n const targetCtr = expectedCtr(opts.curve, opts.targetPosition) / 100;\n const out: StrikingDistanceRow[] = [];\n for (const [query, pages] of groupByQuery(rows, true)) {\n const sorted = [...pages.values()].sort((a, b) => b.impressions - a.impressions);\n const primary = sorted[0]!;\n if (primary.position < opts.minPosition || primary.position > opts.maxPosition) continue;\n if (primary.impressions < opts.minImpressions) continue;\n const metrics = toMetrics(primary);\n out.push({\n query,\n page: primary.page,\n ...metrics,\n other_pages: sorted.length - 1,\n potential_extra_clicks: Math.max(\n 0,\n Math.round(primary.impressions * targetCtr - primary.clicks),\n ),\n });\n }\n return out.sort(\n (a, b) => b.potential_extra_clicks - a.potential_extra_clicks || b.impressions - a.impressions,\n );\n}\n\n// ---------------------------------------------------------------------------------------------\n// CTR opportunities\n// ---------------------------------------------------------------------------------------------\n\nexport interface CtrOpportunityRow extends Metrics {\n key: string;\n expected_ctr_pct: number;\n /** Actual minus expected CTR, in percentage points (negative = underperforming). */\n ctr_gap_pp: number;\n /** Clicks lost versus the expected CTR at this position. */\n missed_clicks: number;\n}\n\nexport function findCtrOpportunities(\n rows: { key: string; clicks: number; impressions: number; ctr: number; position: number }[],\n opts: { maxPosition: number; minImpressions: number; threshold: number; curve: CurvePoint[] },\n): CtrOpportunityRow[] {\n const out: CtrOpportunityRow[] = [];\n for (const row of rows) {\n if (row.impressions < opts.minImpressions || row.position > opts.maxPosition) continue;\n const expected = expectedCtr(opts.curve, row.position);\n const actual = (row.clicks / row.impressions) * 100;\n if (actual >= expected * opts.threshold) continue;\n out.push({\n key: row.key,\n ...toMetrics(row),\n expected_ctr_pct: round(expected, 2),\n ctr_gap_pp: round(actual - expected, 2),\n missed_clicks: Math.round(row.impressions * (expected / 100) - row.clicks),\n });\n }\n return out.sort((a, b) => b.missed_clicks - a.missed_clicks);\n}\n\n// ---------------------------------------------------------------------------------------------\n// Keyword cannibalization\n// ---------------------------------------------------------------------------------------------\n\nexport interface CannibalizationRow {\n query: string;\n clicks: number;\n impressions: number;\n /** Impressions that went to pages other than the top one. */\n contested_impressions: number;\n competing_pages: number;\n pages: PageShare[];\n}\n\nexport function findCannibalization(\n rows: QueryPageRow[],\n opts: { minImpressions: number; minSharePct: number; maxPagesShown?: number },\n): CannibalizationRow[] {\n const out: CannibalizationRow[] = [];\n for (const [query, pages] of groupByQuery(rows, true)) {\n if (pages.size < 2) continue;\n const list = [...pages.values()];\n const impressions = list.reduce((s, r) => s + r.impressions, 0);\n if (impressions < opts.minImpressions) continue;\n const shares: PageShare[] = list\n .map((r) => ({\n page: r.page,\n ...toMetrics(r),\n share_pct: round((r.impressions / impressions) * 100, 1),\n }))\n .sort((a, b) => b.impressions - a.impressions);\n const competing = shares.filter((s) => s.share_pct >= opts.minSharePct);\n if (competing.length < 2) continue;\n out.push({\n query,\n clicks: list.reduce((s, r) => s + r.clicks, 0),\n impressions,\n contested_impressions: impressions - shares[0]!.impressions,\n competing_pages: competing.length,\n pages: shares.slice(0, opts.maxPagesShown ?? 5),\n });\n }\n return out.sort((a, b) => b.contested_impressions - a.contested_impressions);\n}\n","import { z } from 'zod';\nimport { buildCtrCurve, type CurvePoint } from '../analysis/ctr-curve.js';\nimport {\n findCannibalization,\n findCtrOpportunities,\n findStrikingDistance,\n type CannibalizationRow,\n type CtrOpportunityRow,\n type QueryPageRow,\n type StrikingDistanceRow,\n} from '../analysis/opportunities.js';\nimport { compactUrl, fmtInt, fmtPct, fmtPos, mdTable, omittedNote, sections } from '../format.js';\nimport type { SearchAnalyticsRow } from '../gsc/types.js';\nimport {\n dateRangeFields,\n displayHost,\n excludeQueriesField,\n fetchRows,\n limitField,\n propertyFor,\n resolveWindow,\n siteUrlField,\n windowNote,\n withQueryExclusion,\n type ResolvedWindow,\n} from './common.js';\nimport { defineTool, READ_ONLY, type ToolContext } from './types.js';\n\nfunction toQueryPageRows(rows: SearchAnalyticsRow[]): QueryPageRow[] {\n return rows.map((r) => ({\n query: r.keys?.[0] ?? '',\n page: r.keys?.[1] ?? '',\n clicks: r.clicks,\n impressions: r.impressions,\n ctr: r.ctr,\n position: r.position,\n }));\n}\n\nasync function queryPageRows(\n ctx: ToolContext,\n siteUrl: string,\n window: ResolvedWindow,\n excludeQueries?: string,\n) {\n const { rows, truncated } = await fetchRows(ctx, siteUrl, window, ['query', 'page'], {\n filters: withQueryExclusion(undefined, excludeQueries),\n dataState: window.dataState,\n });\n return { rows: toQueryPageRows(rows), truncated };\n}\n\nconst truncatedNote = (truncated: boolean) =>\n truncated\n ? '_Row cap reached: only the top rows by clicks were analysed (raise GSCLAW_MAX_ROWS or shorten the date range)._'\n : null;\n\n// ---------------------------------------------------------------------------------------------\n\nexport interface StrikingDistanceResult {\n site_url: string;\n start_date: string;\n end_date: string;\n data_state: string;\n position_range: [number, number];\n min_impressions: number;\n target_position: number;\n expected_ctr_at_target_pct: number;\n total_matches: number;\n rows: StrikingDistanceRow[];\n truncated: boolean;\n}\n\nexport const strikingDistanceKeywords = defineTool({\n name: 'striking_distance_keywords',\n title: 'Striking-distance keywords',\n description:\n 'Finds queries ranking just off the top spots (default average position 8–20) with meaningful impressions: the cheapest wins in SEO. ' +\n 'Returns the ranking page, position, impressions and an estimate of extra clicks if the page reached position 3 (based on the site’s own CTR curve).',\n input: z.object({\n site_url: siteUrlField,\n ...dateRangeFields,\n min_position: z.number().min(1).max(100).default(8),\n max_position: z.number().min(1).max(100).default(20),\n min_impressions: z\n .number()\n .int()\n .min(0)\n .default(20)\n .describe('Minimum impressions in the period.'),\n target_position: z\n .number()\n .min(1)\n .max(10)\n .default(3)\n .describe('Position used to estimate potential clicks.'),\n exclude_queries: excludeQueriesField,\n limit: limitField(25),\n }),\n annotations: READ_ONLY,\n async run(ctx, input): Promise<StrikingDistanceResult> {\n const property = await propertyFor(ctx, input.site_url);\n const window = await resolveWindow(ctx, property, input);\n const { rows, truncated } = await queryPageRows(\n ctx,\n property.site_url,\n window,\n input.exclude_queries,\n );\n const curve = buildCtrCurve(rows, { benchmark: 'site' });\n const matches = findStrikingDistance(rows, {\n minPosition: input.min_position,\n maxPosition: input.max_position,\n minImpressions: input.min_impressions,\n targetPosition: input.target_position,\n curve,\n });\n return {\n site_url: property.site_url,\n start_date: window.startDate,\n end_date: window.endDate,\n data_state: window.dataState,\n position_range: [input.min_position, input.max_position],\n min_impressions: input.min_impressions,\n target_position: input.target_position,\n expected_ctr_at_target_pct: curve[Math.round(input.target_position) - 1]?.ctr_pct ?? 0,\n total_matches: matches.length,\n rows: matches.slice(0, input.limit),\n truncated,\n };\n },\n format(r) {\n const host = displayHost(r.site_url);\n if (r.total_matches === 0) {\n return `No striking-distance queries for ${r.site_url} (position ${r.position_range[0]}–${r.position_range[1]}, ≥${r.min_impressions} impressions, ${r.start_date} → ${r.end_date}). Try a longer date range or lower min_impressions.`;\n }\n return sections(\n `**${fmtInt(r.total_matches)} striking-distance queries** for ${r.site_url} (avg position ${r.position_range[0]}–${r.position_range[1]}, ≥${r.min_impressions} impressions)`,\n windowNote({\n startDate: r.start_date,\n endDate: r.end_date,\n dataState: r.data_state === 'all' ? 'all' : 'final',\n label: '',\n }),\n mdTable(\n [\n 'Query',\n 'Ranking page',\n 'Pos.',\n 'Impr.',\n 'Clicks',\n 'CTR',\n `+Clicks @#${r.target_position}`,\n ],\n r.rows.map((row) => [\n row.query,\n compactUrl(row.page, host) + (row.other_pages ? ` (+${row.other_pages} other)` : ''),\n fmtPos(row.position),\n fmtInt(row.impressions),\n fmtInt(row.clicks),\n fmtPct(row.ctr_pct),\n `+${fmtInt(row.potential_extra_clicks)}`,\n ]),\n ),\n omittedNote(r.rows.length, r.total_matches, 'Raise `limit` to see more.'),\n `_Potential clicks assume ${fmtPct(r.expected_ctr_at_target_pct)} CTR at position ${r.target_position} (your site's CTR curve where data allows, otherwise an industry average). \"+N other\" = more pages competing for the query; see keyword_cannibalization._`,\n truncatedNote(r.truncated),\n );\n },\n});\n\n// ---------------------------------------------------------------------------------------------\n\nexport interface CtrOpportunitiesResult {\n site_url: string;\n start_date: string;\n end_date: string;\n data_state: string;\n dimension: 'query' | 'page';\n benchmark: 'site' | 'industry';\n threshold_pct: number;\n min_impressions: number;\n max_position: number;\n curve: CurvePoint[];\n total_matches: number;\n rows: CtrOpportunityRow[];\n truncated: boolean;\n}\n\nexport const ctrOpportunities = defineTool({\n name: 'ctr_opportunities',\n title: 'CTR opportunities',\n description:\n 'Finds queries or pages with many impressions but a CTR well below what their position usually earns: candidates for better titles, meta descriptions and rich results. ' +\n 'Compares against a CTR-by-position curve (the site’s own by default, falling back to an industry curve) and returns the curve used plus estimated missed clicks.',\n input: z.object({\n site_url: siteUrlField,\n ...dateRangeFields,\n dimension: z.enum(['query', 'page']).default('query'),\n max_position: z\n .number()\n .min(1)\n .max(20)\n .default(10)\n .describe('Only consider rows ranking at or above this average position.'),\n min_impressions: z.number().int().min(0).default(100),\n benchmark: z\n .enum(['site', 'industry'])\n .default('site')\n .describe(\n 'site = your own median CTR per position (industry values fill gaps); industry = a generic curve from public CTR studies.',\n ),\n threshold_pct: z\n .number()\n .min(1)\n .max(100)\n .default(60)\n .describe('Flag rows whose CTR is below this % of the expected CTR (default 60%).'),\n exclude_queries: excludeQueriesField,\n limit: limitField(25),\n }),\n annotations: READ_ONLY,\n async run(ctx, input): Promise<CtrOpportunitiesResult> {\n const property = await propertyFor(ctx, input.site_url);\n const window = await resolveWindow(ctx, property, input);\n const { rows, truncated } = await fetchRows(ctx, property.site_url, window, [input.dimension], {\n filters: withQueryExclusion(undefined, input.exclude_queries),\n dataState: window.dataState,\n });\n const keyed = rows.map((r) => ({ key: r.keys?.[0] ?? '', ...r }));\n const curve = buildCtrCurve(keyed, { benchmark: input.benchmark });\n const matches = findCtrOpportunities(keyed, {\n maxPosition: input.max_position,\n minImpressions: input.min_impressions,\n threshold: input.threshold_pct / 100,\n curve,\n });\n return {\n site_url: property.site_url,\n start_date: window.startDate,\n end_date: window.endDate,\n data_state: window.dataState,\n dimension: input.dimension,\n benchmark: input.benchmark,\n threshold_pct: input.threshold_pct,\n min_impressions: input.min_impressions,\n max_position: input.max_position,\n curve: curve.slice(0, Math.max(10, Math.ceil(input.max_position))),\n total_matches: matches.length,\n rows: matches.slice(0, input.limit),\n truncated,\n };\n },\n format(r) {\n const host = displayHost(r.site_url);\n const curve = r.curve\n .map((p) => `#${p.position}: ${fmtPct(p.ctr_pct)}${p.source === 'industry' ? '*' : ''}`)\n .join(' · ');\n if (r.total_matches === 0) {\n return sections(\n `No CTR opportunities for ${r.site_url}: no ${r.dimension} with ≥${r.min_impressions} impressions at position ≤${r.max_position} has a CTR below ${r.threshold_pct}% of the expected value.`,\n `_Benchmark curve: ${curve}_`,\n );\n }\n return sections(\n `**${fmtInt(r.total_matches)} ${r.dimension === 'query' ? 'queries' : 'pages'} with below-expected CTR** for ${r.site_url} (CTR < ${r.threshold_pct}% of the ${r.benchmark} benchmark for their position)`,\n windowNote({\n startDate: r.start_date,\n endDate: r.end_date,\n dataState: r.data_state === 'all' ? 'all' : 'final',\n label: '',\n }),\n mdTable(\n [\n r.dimension === 'page' ? 'Page' : 'Query',\n 'Pos.',\n 'Impr.',\n 'Clicks',\n 'CTR',\n 'Expected',\n 'Missed clicks',\n ],\n r.rows.map((row) => [\n r.dimension === 'page' ? compactUrl(row.key, host) : row.key,\n fmtPos(row.position),\n fmtInt(row.impressions),\n fmtInt(row.clicks),\n fmtPct(row.ctr_pct),\n fmtPct(row.expected_ctr_pct),\n fmtInt(row.missed_clicks),\n ]),\n ),\n omittedNote(r.rows.length, r.total_matches, 'Raise `limit` to see more.'),\n `_Benchmark CTR by position (${r.benchmark}; * = industry fallback): ${curve}. Branded queries inflate CTR at top positions; use exclude_queries for a non-brand view._`,\n truncatedNote(r.truncated),\n );\n },\n});\n\n// ---------------------------------------------------------------------------------------------\n\nexport interface CannibalizationResult {\n site_url: string;\n start_date: string;\n end_date: string;\n data_state: string;\n min_impressions: number;\n min_share_pct: number;\n total_matches: number;\n rows: CannibalizationRow[];\n truncated: boolean;\n}\n\nexport const keywordCannibalization = defineTool({\n name: 'keyword_cannibalization',\n title: 'Keyword cannibalization',\n description:\n \"Finds queries where two or more of the site's pages each get a meaningful share of impressions, so they compete with each other. Returns each page's impression share, clicks and position. URL #fragments are merged into their page.\",\n input: z.object({\n site_url: siteUrlField,\n ...dateRangeFields,\n min_impressions: z\n .number()\n .int()\n .min(0)\n .default(50)\n .describe('Minimum total impressions for the query.'),\n min_share_pct: z\n .number()\n .min(1)\n .max(50)\n .default(10)\n .describe(\n 'A page counts as competing if it has at least this share of the query’s impressions.',\n ),\n exclude_queries: excludeQueriesField,\n limit: limitField(20),\n }),\n annotations: READ_ONLY,\n async run(ctx, input): Promise<CannibalizationResult> {\n const property = await propertyFor(ctx, input.site_url);\n const window = await resolveWindow(ctx, property, input);\n const { rows, truncated } = await queryPageRows(\n ctx,\n property.site_url,\n window,\n input.exclude_queries,\n );\n const matches = findCannibalization(rows, {\n minImpressions: input.min_impressions,\n minSharePct: input.min_share_pct,\n });\n return {\n site_url: property.site_url,\n start_date: window.startDate,\n end_date: window.endDate,\n data_state: window.dataState,\n min_impressions: input.min_impressions,\n min_share_pct: input.min_share_pct,\n total_matches: matches.length,\n rows: matches.slice(0, input.limit),\n truncated,\n };\n },\n format(r) {\n const host = displayHost(r.site_url);\n if (r.total_matches === 0) {\n return `No cannibalization found for ${r.site_url}: no query with ≥${r.min_impressions} impressions has 2+ pages each above ${r.min_share_pct}% share (${r.start_date} → ${r.end_date}).`;\n }\n const blocks = r.rows.map((q) =>\n sections(\n `**\"${q.query}\"** — ${fmtInt(q.impressions)} impr., ${fmtInt(q.clicks)} clicks, ${q.competing_pages} competing pages`,\n mdTable(\n ['Page', 'Share', 'Impr.', 'Clicks', 'Pos.'],\n q.pages.map((p) => [\n compactUrl(p.page, host),\n fmtPct(p.share_pct),\n fmtInt(p.impressions),\n fmtInt(p.clicks),\n fmtPos(p.position),\n ]),\n ),\n ),\n );\n return sections(\n `**${fmtInt(r.total_matches)} cannibalized queries** for ${r.site_url} (sorted by impressions not going to the top page)`,\n windowNote({\n startDate: r.start_date,\n endDate: r.end_date,\n dataState: r.data_state === 'all' ? 'all' : 'final',\n label: '',\n }),\n ...blocks,\n omittedNote(r.rows.length, r.total_matches, 'Raise `limit` to see more.'),\n truncatedNote(r.truncated),\n );\n },\n});\n","import { z } from 'zod';\nimport {\n compareRows,\n metricChange,\n topByCurrentClicks,\n type ComparedRow,\n type MetricChange,\n} from '../analysis/compare.js';\nimport { COMPARE_MODES, comparisonRange, type CompareMode, type DateRange } from '../dates.js';\nimport { publicErrorMessage, UserFacingError } from '../errors.js';\nimport {\n fmtChange,\n fmtInt,\n fmtPct,\n fmtPos,\n mdTable,\n sections,\n toMetrics,\n type Metrics,\n} from '../format.js';\nimport { absolutePageUrl, type Property } from '../gsc/sites.js';\nimport type { DimensionFilter } from '../gsc/types.js';\nimport { positionChange } from './compare.js';\nimport {\n dateRangeFields,\n fetchKeyed,\n fetchRows,\n fetchTotals,\n limitField,\n propertyFor,\n resolveWindow,\n type ResolvedWindow,\n} from './common.js';\nimport {\n formatInspection,\n inspectOne,\n propertyForUrl,\n type InspectionSummary,\n} from './inspection.js';\nimport { defineTool, READ_ONLY, type ToolContext } from './types.js';\n\nexport interface PageReportResult {\n site_url: string;\n page: string;\n current: DateRange;\n previous: DateRange;\n compare_to: CompareMode;\n data_state: 'final' | 'all';\n totals: { current: Metrics; previous: Metrics; change: MetricChange };\n trend: ({ date: string } & Metrics)[];\n top_queries: ComparedRow[];\n query_count: number;\n inspection: InspectionSummary | null;\n inspection_error: string | null;\n}\n\n/** Turns a path like '/pricing' into the page URL Search Console reports, using the page's own data. */\nasync function resolvePageUrl(\n ctx: ToolContext,\n property: Property,\n page: string,\n window: ResolvedWindow,\n): Promise<string> {\n const direct = absolutePageUrl(property, page);\n if (direct) return direct;\n if (!page.startsWith('/'))\n throw new UserFacingError(`'${page}' is not a URL or a path starting with '/'.`);\n const { rows } = await fetchRows(ctx, property.site_url, window, ['page'], {\n filters: [{ dimension: 'page', operator: 'contains', expression: page }],\n maxRows: 1000,\n });\n const candidates = rows\n .map((r) => ({ url: r.keys?.[0] ?? '', impressions: r.impressions }))\n .filter((c) => {\n try {\n return new URL(c.url).pathname === page;\n } catch {\n return false;\n }\n })\n .sort((a, b) => b.impressions - a.impressions);\n if (!candidates[0]) {\n throw new UserFacingError(\n `No page with path '${page}' has Search data on ${property.site_url} in this period. Pass the full URL (e.g. https://example.com${page}).`,\n 'not_found',\n );\n }\n return candidates[0].url;\n}\n\nexport const pageReport = defineTool({\n name: 'page_report',\n title: 'Page report',\n description:\n \"One-call report for a single page: clicks/impressions/CTR/position vs the previous period, a daily trend, the page's top queries with deltas, and its URL Inspection status (index state, canonical, last crawl).\",\n input: z.object({\n page: z\n .string()\n .min(1)\n .describe(\"Full page URL, or a path like '/pricing' (then site_url is required).\"),\n site_url: z.string().optional().describe('Property. Optional when page is a full URL.'),\n ...dateRangeFields,\n compare_to: z.enum(COMPARE_MODES).default('previous_period'),\n top_queries: limitField(15, 100),\n include_inspection: z\n .boolean()\n .default(true)\n .describe('Also run URL Inspection (uses 1 of 2,000 daily inspections).'),\n }),\n annotations: READ_ONLY,\n async run(ctx, input): Promise<PageReportResult> {\n let property: Property;\n if (/^https?:\\/\\//i.test(input.page))\n property = await propertyForUrl(ctx.gsc, input.page, input.site_url);\n else if (input.site_url) property = await propertyFor(ctx, input.site_url);\n else throw new UserFacingError('Pass site_url when page is a path.');\n\n const current = await resolveWindow(ctx, property, input);\n const previous = comparisonRange(current, input.compare_to);\n const pageUrl = await resolvePageUrl(ctx, property, input.page, current);\n const filters: DimensionFilter[] = [\n { dimension: 'page', operator: 'equals', expression: pageUrl },\n ];\n const opts = { filters, dataState: current.dataState };\n\n const inspection = input.include_inspection\n ? inspectOne(ctx, pageUrl, property.site_url).then(\n (value) => ({ value, error: null }),\n (error: unknown) => ({\n value: null,\n error: publicErrorMessage(error).message.split('\\n')[0]!,\n }),\n )\n : Promise.resolve({ value: null, error: null });\n\n const [curTotals, prevTotals, trendRows, curQueries, prevQueries, inspected] =\n await Promise.all([\n fetchTotals(ctx, property.site_url, current, opts),\n fetchTotals(ctx, property.site_url, previous, opts),\n fetchRows(ctx, property.site_url, current, ['date'], opts),\n fetchKeyed(ctx, property.site_url, current, 'query', { ...opts, maxRows: 5000 }),\n fetchKeyed(ctx, property.site_url, previous, 'query', { ...opts, maxRows: 5000 }),\n inspection,\n ]);\n\n const cur = toMetrics(curTotals);\n const prev = toMetrics(prevTotals);\n const queries = compareRows(curQueries.rows, prevQueries.rows);\n return {\n site_url: property.site_url,\n page: pageUrl,\n current: { startDate: current.startDate, endDate: current.endDate },\n previous,\n compare_to: input.compare_to,\n data_state: current.dataState,\n totals: { current: cur, previous: prev, change: metricChange(cur, prev) },\n trend: trendRows.rows\n .map((r) => ({ date: r.keys?.[0] ?? '', ...toMetrics(r) }))\n .sort((a, b) => a.date.localeCompare(b.date)),\n top_queries: topByCurrentClicks(queries, input.top_queries),\n query_count: curQueries.rows.size,\n inspection: inspected.value,\n inspection_error: inspected.error,\n };\n },\n format(r) {\n const t = r.totals;\n const weekly = summarizeTrend(r.trend);\n return sections(\n `**Page report:** ${r.page}`,\n `_${r.current.startDate} → ${r.current.endDate} vs ${r.previous.startDate} → ${r.previous.endDate} (${r.compare_to === 'year_over_year' ? 'year over year' : 'previous period'}, ${r.data_state} data)._`,\n mdTable(\n ['Metric', 'Current', 'Previous', 'Change'],\n [\n [\n 'Clicks',\n fmtInt(t.current.clicks),\n fmtInt(t.previous.clicks),\n fmtChange(t.current.clicks, t.previous.clicks),\n ],\n [\n 'Impressions',\n fmtInt(t.current.impressions),\n fmtInt(t.previous.impressions),\n fmtChange(t.current.impressions, t.previous.impressions),\n ],\n [\n 'CTR',\n fmtPct(t.current.ctr_pct),\n fmtPct(t.previous.ctr_pct),\n `${t.change.ctr_pp >= 0 ? '+' : ''}${t.change.ctr_pp.toFixed(2)} pp`,\n ],\n [\n 'Avg position',\n fmtPos(t.current.position),\n fmtPos(t.previous.position),\n positionChange(t.change.rank_improvement),\n ],\n ],\n ),\n weekly ? `**Trend (clicks per 7 days, oldest → newest):** ${weekly}` : null,\n r.top_queries.length\n ? sections(\n `**Top queries** (${fmtInt(r.query_count)} total)`,\n mdTable(\n ['Query', 'Clicks', 'Δ clicks', 'Impr.', 'CTR', 'Pos.', 'Δ pos.'],\n r.top_queries.map((q) => [\n q.key,\n fmtInt(q.current.clicks),\n fmtChange(q.current.clicks, q.previous.clicks),\n fmtInt(q.current.impressions),\n fmtPct(q.current.ctr_pct),\n fmtPos(q.current.position),\n positionChange(q.change.rank_improvement),\n ]),\n ),\n )\n : '_No query data for this page in the period._',\n r.inspection\n ? formatInspection(r.inspection)\n : r.inspection_error\n ? `_URL Inspection failed: ${r.inspection_error}_`\n : null,\n );\n },\n});\n\nfunction summarizeTrend(trend: ({ date: string } & Metrics)[]): string | null {\n if (trend.length < 7) return trend.length ? trend.map((d) => fmtInt(d.clicks)).join(' → ') : null;\n const buckets: number[] = [];\n // Bucket from the newest day backwards so the last bucket is a full week.\n for (let end = trend.length; end > 0; end -= 7) {\n const slice = trend.slice(Math.max(0, end - 7), end);\n if (slice.length === 7) buckets.unshift(slice.reduce((s, d) => s + d.clicks, 0));\n }\n return buckets.map(fmtInt).join(' → ');\n}\n","import { z } from 'zod';\nimport { UserFacingError } from '../errors.js';\nimport {\n compactUrl,\n fmtInt,\n fmtPct,\n fmtPos,\n mdTable,\n omittedNote,\n sections,\n toMetrics,\n type Metrics,\n} from '../format.js';\nimport {\n AGGREGATION_TYPES,\n DIMENSIONS,\n type Dimension,\n type SearchAnalyticsRequest,\n} from '../gsc/types.js';\nimport {\n dateRangeFields,\n displayHost,\n filtersField,\n limitField,\n propertyFor,\n resolveWindow,\n searchTypeField,\n siteUrlField,\n toFilterGroups,\n windowNote,\n} from './common.js';\nimport { defineTool, READ_ONLY } from './types.js';\n\nconst SORT_KEYS = ['clicks', 'impressions', 'ctr', 'position'] as const;\n\nexport interface AnalyticsRow extends Metrics {\n keys: Partial<Record<Dimension, string>>;\n}\n\nexport interface SearchAnalyticsResult {\n site_url: string;\n start_date: string;\n end_date: string;\n data_state: string;\n search_type: string;\n dimensions: Dimension[];\n totals: Metrics | null;\n rows: AnalyticsRow[];\n total_rows: number;\n /** True when the row cap was reached and more rows may exist. */\n truncated: boolean;\n first_incomplete_date?: string;\n}\n\nexport const searchAnalytics = defineTool({\n name: 'search_analytics',\n title: 'Search performance (raw query)',\n description:\n 'Queries Search Console performance data (clicks, impressions, CTR, average position) grouped by any dimensions, with filters (equals, contains, regex…), search type and aggregation type. Auto-paginates up to row_limit. ' +\n 'Use for custom breakdowns; prefer the analysis tools (compare_periods, striking_distance_keywords, …) for common questions. ' +\n 'Note: the API exposes only top rows (max ~50K/day/search type), and anonymized rare queries are omitted from query rows but included in totals.',\n input: z.object({\n site_url: siteUrlField,\n ...dateRangeFields,\n dimensions: z\n .array(z.enum(DIMENSIONS))\n .max(5)\n .default(['query'])\n .describe(\n 'Group by. searchAppearance cannot be combined with other dimensions. hour requires recent dates (last ~10 days) and switches to hourly data. Use [] for property totals.',\n ),\n filters: filtersField,\n search_type: searchTypeField,\n aggregation_type: z\n .enum(AGGREGATION_TYPES)\n .default('auto')\n .describe(\n 'auto (default), byPage, byProperty (not with page dimension/filter), byNewsShowcasePanel.',\n ),\n row_limit: z\n .number()\n .int()\n .min(1)\n .max(500_000)\n .default(1000)\n .describe(\n 'Rows to fetch from Google (auto-paginated in 25K pages; capped by the server’s GSCLAW_MAX_ROWS).',\n ),\n order_by: z\n .enum(SORT_KEYS)\n .default('clicks')\n .describe('Sort shown rows by this metric (position sorts ascending).'),\n limit: limitField(25),\n include_totals: z\n .boolean()\n .default(true)\n .describe('Also fetch property totals for the same filters (one extra request).'),\n }),\n annotations: READ_ONLY,\n async run(ctx, input): Promise<SearchAnalyticsResult> {\n if (input.dimensions.includes('searchAppearance') && input.dimensions.length > 1) {\n throw new UserFacingError(\n 'searchAppearance cannot be combined with other dimensions. Query searchAppearance alone first, then filter on one appearance and group by other dimensions.',\n );\n }\n const property = await propertyFor(ctx, input.site_url);\n const window = await resolveWindow(ctx, property, input, input.search_type);\n const hourly = input.dimensions.includes('hour');\n const base: Omit<SearchAnalyticsRequest, 'rowLimit' | 'startRow'> = {\n startDate: window.startDate,\n endDate: window.endDate,\n type: input.search_type,\n dimensionFilterGroups: toFilterGroups(input.filters),\n aggregationType: input.aggregation_type,\n dataState: hourly ? 'hourly_all' : window.dataState,\n };\n\n const maxRows = Math.min(input.row_limit, ctx.config.maxRows);\n const [result, totalsRes] = await Promise.all([\n input.dimensions.length > 0\n ? ctx.gsc.searchAnalyticsAll(\n property.site_url,\n { ...base, dimensions: input.dimensions },\n maxRows,\n )\n : Promise.resolve(null),\n input.include_totals || input.dimensions.length === 0\n ? ctx.gsc.searchAnalytics(property.site_url, { ...base, rowLimit: 1 })\n : Promise.resolve(null),\n ]);\n\n const rows: AnalyticsRow[] = (result?.rows ?? []).map((r) => ({\n keys: Object.fromEntries(input.dimensions.map((d, i) => [d, r.keys?.[i] ?? ''])),\n ...toMetrics(r),\n }));\n sortRows(rows, input.order_by);\n\n const totalRow = totalsRes?.rows?.[0];\n const meta = result?.metadata ?? totalsRes?.metadata;\n return {\n site_url: property.site_url,\n start_date: window.startDate,\n end_date: window.endDate,\n data_state: base.dataState ?? 'final',\n search_type: input.search_type,\n dimensions: input.dimensions,\n totals: totalRow\n ? toMetrics(totalRow)\n : totalsRes\n ? toMetrics({ clicks: 0, impressions: 0 })\n : null,\n rows: rows.slice(0, input.limit),\n total_rows: rows.length,\n truncated: result?.truncated ?? false,\n first_incomplete_date: meta?.first_incomplete_date ?? meta?.firstIncompleteDate,\n };\n },\n format(r) {\n const host = displayHost(r.site_url);\n const totals = r.totals\n ? `**Totals:** ${fmtInt(r.totals.clicks)} clicks · ${fmtInt(r.totals.impressions)} impressions · CTR ${fmtPct(r.totals.ctr_pct)} · avg position ${fmtPos(r.totals.position)}`\n : null;\n const table =\n r.dimensions.length > 0\n ? mdTable(\n [...r.dimensions, 'Clicks', 'Impr.', 'CTR', 'Pos.'],\n r.rows.map((row) => [\n ...r.dimensions.map((d) =>\n d === 'page' ? compactUrl(row.keys[d] ?? '', host) : row.keys[d],\n ),\n fmtInt(row.clicks),\n fmtInt(row.impressions),\n fmtPct(row.ctr_pct),\n fmtPos(row.position),\n ]),\n )\n : null;\n return sections(\n `**Search analytics** for ${r.site_url} (${r.search_type}${r.dimensions.length ? `, by ${r.dimensions.join(' × ')}` : ''})`,\n windowNote({\n startDate: r.start_date,\n endDate: r.end_date,\n dataState: r.data_state === 'final' ? 'final' : 'all',\n label: '',\n }),\n totals,\n table,\n omittedNote(r.rows.length, r.total_rows, 'Raise `limit` to see more, or add filters.'),\n r.truncated\n ? `_Row cap reached (${fmtInt(r.total_rows)} rows fetched); more rows may exist. Raise row_limit or narrow filters/dates._`\n : null,\n r.first_incomplete_date\n ? `_Data from ${r.first_incomplete_date} onward is incomplete._`\n : null,\n );\n },\n});\n\nfunction sortRows(rows: AnalyticsRow[], key: (typeof SORT_KEYS)[number]): void {\n const value = (r: AnalyticsRow) =>\n key === 'ctr' ? r.ctr_pct : key === 'position' ? -(r.position ?? Infinity) : r[key];\n rows.sort((a, b) => value(b) - value(a));\n}\n","import { z } from 'zod';\nimport { listProperties, type Property } from '../gsc/sites.js';\nimport { mdTable, sections } from '../format.js';\nimport { defineTool, READ_ONLY } from './types.js';\n\nexport const listSites = defineTool({\n name: 'list_sites',\n title: 'List Search Console properties',\n description:\n \"Lists every Search Console property this server can access, with its type (domain 'sc-domain:' vs URL-prefix) and permission level. Call this first to find the exact property to pass as site_url.\",\n input: z.object({}),\n annotations: READ_ONLY,\n async run(ctx): Promise<{ identity: string; properties: Property[] }> {\n return { identity: ctx.gsc.identity.email, properties: await listProperties(ctx.gsc) };\n },\n format(result) {\n if (result.properties.length === 0) {\n return `No Search Console properties are visible to ${result.identity}. Add it as a user on your properties: Search Console → Settings → Users and permissions → Add user.`;\n }\n const table = mdTable(\n ['Property', 'Type', 'Permission'],\n result.properties.map((p) => [\n p.site_url,\n p.type === 'domain' ? 'domain' : 'URL prefix',\n p.has_access ? p.permission_level : 'unverified (no data)',\n ]),\n );\n return sections(\n `**${result.properties.length} properties** visible to ${result.identity}:`,\n table,\n );\n },\n});\n","import { z } from 'zod';\nimport { UserFacingError } from '../errors.js';\nimport { fmtInt, mdTable, sections } from '../format.js';\nimport type { Sitemap } from '../gsc/types.js';\nimport { propertyFor, siteUrlField } from './common.js';\nimport { defineTool, READ_ONLY } from './types.js';\n\nexport interface SitemapSummary {\n path: string;\n type: string | null;\n is_index: boolean;\n is_pending: boolean;\n last_submitted: string | null;\n last_downloaded: string | null;\n errors: number;\n warnings: number;\n /** URLs submitted per content type. Google no longer reports indexed counts for sitemaps. */\n submitted: number;\n contents: { type: string; submitted: number }[];\n}\n\nconst num = (v: string | number | undefined) => (v === undefined ? 0 : Number(v) || 0);\n\nexport function summarizeSitemap(s: Sitemap): SitemapSummary {\n const contents = (s.contents ?? []).map((c) => ({\n type: c.type ?? 'web',\n submitted: num(c.submitted),\n }));\n return {\n path: s.path,\n type: s.type ?? null,\n is_index: Boolean(s.isSitemapsIndex),\n is_pending: Boolean(s.isPending),\n last_submitted: s.lastSubmitted ?? null,\n last_downloaded: s.lastDownloaded ?? null,\n errors: num(s.errors),\n warnings: num(s.warnings),\n submitted: contents.reduce((sum, c) => sum + c.submitted, 0),\n contents,\n };\n}\n\nconst INDEXED_NOTE =\n '_Google no longer reports per-sitemap indexed counts (the API field is deprecated). Use inspect_url / batch_inspect_urls to check index status of specific URLs._';\n\nfunction sitemapTable(sitemaps: SitemapSummary[]): string {\n return mdTable(\n ['Sitemap', 'Type', 'Submitted URLs', 'Errors', 'Warnings', 'Last read', 'Status'],\n sitemaps.map((s) => [\n s.path,\n s.is_index ? 'index' : (s.type ?? '—'),\n fmtInt(s.submitted),\n s.errors,\n s.warnings,\n s.last_downloaded?.slice(0, 10) ?? 'never',\n s.is_pending ? 'pending' : s.errors > 0 ? 'has errors' : 'ok',\n ]),\n );\n}\n\nexport const listSitemaps = defineTool({\n name: 'list_sitemaps',\n title: 'List sitemaps',\n description:\n 'Lists sitemaps submitted for a property with status, errors, warnings, submitted URL counts and when Google last read them. Pass sitemap_index to list the children of a sitemap index.',\n input: z.object({\n site_url: siteUrlField,\n sitemap_index: z\n .string()\n .url()\n .optional()\n .describe('Full URL of a sitemap index to list its child sitemaps.'),\n }),\n annotations: READ_ONLY,\n async run(ctx, input) {\n const property = await propertyFor(ctx, input.site_url);\n const sitemaps = await ctx.gsc.listSitemaps(property.site_url, input.sitemap_index);\n return { site_url: property.site_url, sitemaps: sitemaps.map(summarizeSitemap) };\n },\n format(r) {\n if (r.sitemaps.length === 0) {\n return `No sitemaps are submitted for ${r.site_url}. Submit one in Search Console → Sitemaps (or with submit_sitemap if writes are enabled).`;\n }\n const withErrors = r.sitemaps.filter((s) => s.errors > 0).length;\n return sections(\n `**${r.sitemaps.length} sitemap(s)** for ${r.site_url}${withErrors ? ` — ${withErrors} with errors` : ''}:`,\n sitemapTable(r.sitemaps),\n INDEXED_NOTE,\n );\n },\n});\n\nexport const getSitemap = defineTool({\n name: 'get_sitemap',\n title: 'Get sitemap details',\n description:\n 'Shows one sitemap: status, errors, warnings, submitted URL counts per content type and last download time.',\n input: z.object({\n site_url: siteUrlField,\n sitemap_url: z\n .string()\n .url()\n .describe('Full sitemap URL exactly as submitted (see list_sitemaps).'),\n }),\n annotations: READ_ONLY,\n async run(ctx, input) {\n const property = await propertyFor(ctx, input.site_url);\n return {\n site_url: property.site_url,\n sitemap: summarizeSitemap(await ctx.gsc.getSitemap(property.site_url, input.sitemap_url)),\n };\n },\n format(r) {\n const s = r.sitemap;\n const contents = s.contents.map((c) => `${c.type}: ${fmtInt(c.submitted)}`).join(' · ');\n return sections(\n `**Sitemap** ${s.path}`,\n [\n `- Type: ${s.is_index ? 'sitemap index' : (s.type ?? 'unknown')}${s.is_pending ? ' (processing pending)' : ''}`,\n `- Submitted URLs: ${fmtInt(s.submitted)}${contents ? ` (${contents})` : ''}`,\n `- Errors: ${s.errors} · Warnings: ${s.warnings}`,\n `- Last submitted: ${s.last_submitted ?? 'unknown'} · Last read by Google: ${s.last_downloaded ?? 'never'}`,\n ].join('\\n'),\n INDEXED_NOTE,\n );\n },\n});\n\nconst WRITE_NOTE =\n 'Requires GSCLAW_ALLOW_WRITES=true and Full (not Restricted) permission on the property.';\n\nexport const submitSitemap = defineTool({\n name: 'submit_sitemap',\n title: 'Submit sitemap (writes to Search Console)',\n description: `MUTATES STATE: submits (or resubmits) a sitemap URL to Search Console for the property, asking Google to crawl it. ${WRITE_NOTE}`,\n input: z.object({\n site_url: siteUrlField,\n sitemap_url: z.string().url().describe('Full sitemap URL to submit. Must be on the property.'),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n },\n write: true,\n async run(ctx, input) {\n const property = await propertyFor(ctx, input.site_url);\n if (property.permission_level === 'restricted') {\n throw new UserFacingError(\n `Restricted users cannot submit sitemaps. Give ${ctx.gsc.identity.email} Full permission on '${property.site_url}' in Search Console → Settings → Users and permissions.`,\n 'permission',\n );\n }\n await ctx.gsc.submitSitemap(property.site_url, input.sitemap_url);\n return { site_url: property.site_url, sitemap_url: input.sitemap_url, submitted: true };\n },\n format(r) {\n return `Submitted ${r.sitemap_url} for ${r.site_url}. Google will fetch it shortly; check status later with get_sitemap.`;\n },\n});\n\nexport const deleteSitemap = defineTool({\n name: 'delete_sitemap',\n title: 'Delete sitemap (writes to Search Console)',\n description: `MUTATES STATE (destructive): removes a submitted sitemap from Search Console for the property. This does not delete the file from your site. ${WRITE_NOTE}`,\n input: z.object({\n site_url: siteUrlField,\n sitemap_url: z\n .string()\n .url()\n .describe('Full sitemap URL to remove, exactly as listed by list_sitemaps.'),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n write: true,\n async run(ctx, input) {\n const property = await propertyFor(ctx, input.site_url);\n await ctx.gsc.deleteSitemap(property.site_url, input.sitemap_url);\n return { site_url: property.site_url, sitemap_url: input.sitemap_url, deleted: true };\n },\n format(r) {\n return `Removed ${r.sitemap_url} from Search Console for ${r.site_url}. The file on your site is unchanged.`;\n },\n});\n","import { comparePeriods } from './compare.js';\nimport { contentDecay } from './content-decay.js';\nimport { batchInspectUrls, inspectUrl } from './inspection.js';\nimport {\n ctrOpportunities,\n keywordCannibalization,\n strikingDistanceKeywords,\n} from './opportunities.js';\nimport { pageReport } from './page-report.js';\nimport { searchAnalytics } from './search-analytics.js';\nimport { listSites } from './sites.js';\nimport { deleteSitemap, getSitemap, listSitemaps, submitSitemap } from './sitemaps.js';\nimport type { AnyTool } from './types.js';\n\n/** Every tool, in the order clients list them. */\nexport const ALL_TOOLS: AnyTool[] = [\n listSites,\n searchAnalytics,\n comparePeriods,\n strikingDistanceKeywords,\n ctrOpportunities,\n keywordCannibalization,\n contentDecay,\n pageReport,\n inspectUrl,\n batchInspectUrls,\n listSitemaps,\n getSitemap,\n submitSitemap,\n deleteSitemap,\n] as AnyTool[];\n\n/** Tools available for a request: write tools only when enabled and the credentials can write. */\nexport function availableTools(opts: { allowWrites: boolean; canWrite: boolean }): AnyTool[] {\n return ALL_TOOLS.filter((tool) => !tool.write || (opts.allowWrites && opts.canWrite));\n}\n\nexport function findTool(name: string): AnyTool | undefined {\n return ALL_TOOLS.find((tool) => tool.name === name);\n}\n","import { ActivityLog } from './activity.js';\nimport type { Config } from './config.js';\nimport { GscClient, createResponseCache } from './gsc/client.js';\nimport {\n SCOPE_READONLY,\n SCOPE_READWRITE,\n ServiceAccountTokenSource,\n UserTokenSource,\n type UserTokenCache,\n} from './gsc/auth.js';\nimport { createLogger, type Logger, type LogWriter } from './log.js';\nimport { refreshGoogleToken } from './oauth/google.js';\nimport { SCOPE_WRITE, type Principal } from './principal.js';\nimport { availableTools } from './tools/registry.js';\nimport type { AnyTool, ToolContext } from './tools/types.js';\n\nexport interface RuntimeOptions {\n /** Outbound fetch (tests inject a mock Google API here). */\n fetch?: typeof fetch;\n logWriter?: LogWriter;\n now?: () => Date;\n logFields?: Record<string, unknown>;\n}\n\n/** What one caller can do: a Search Console client for their identity and the tools they may use. */\nexport interface Session {\n gsc: GscClient;\n ctx: ToolContext;\n tools: AnyTool[];\n}\n\n/** Everything a transport needs to serve tools for one configured deployment. */\nexport interface Runtime {\n config: Config;\n logger: Logger;\n activity: ActivityLog;\n /** Service-account mode: the deployment owner's session (also used by stdio). */\n owner?: Session;\n /** The session for an authenticated caller. */\n sessionFor(principal: Principal): Session;\n}\n\nexport function createRuntime(config: Config, opts: RuntimeOptions = {}): Runtime {\n const logger = createLogger({\n level: config.logLevel,\n write: opts.logWriter,\n base: opts.logFields,\n });\n const now = opts.now ?? (() => new Date());\n const cache = createResponseCache();\n const userTokens: UserTokenCache = new Map();\n\n const sessionWith = (gsc: GscClient, principal?: Principal): Session => {\n const canWrite = gsc.canWrite && (!principal || principal.scopes.includes(SCOPE_WRITE));\n return {\n gsc,\n ctx: { gsc, config, logger, now },\n tools: availableTools({ allowWrites: config.allowWrites, canWrite }),\n };\n };\n\n let owner: Session | undefined;\n if (config.authMode === 'service_account') {\n if (!config.serviceAccount)\n throw new Error('Service-account mode requires a service account key.');\n const scopes = [config.allowWrites ? SCOPE_READWRITE : SCOPE_READONLY];\n const tokenSource = new ServiceAccountTokenSource(config.serviceAccount, scopes, {\n fetch: opts.fetch,\n });\n owner = sessionWith(\n new GscClient({\n tokenSource,\n logger,\n fetch: opts.fetch,\n cache,\n cacheTtlMs: config.cacheTtlSeconds * 1000,\n }),\n );\n }\n\n return {\n config,\n logger,\n activity: new ActivityLog(),\n owner,\n sessionFor(principal) {\n if (config.authMode === 'service_account') return sessionWith(owner!.gsc, principal);\n\n const google = principal.google;\n const oauth = config.googleOAuth;\n if (principal.kind !== 'user' || !google || !principal.email || !oauth) {\n throw new Error('OAuth mode requires a signed-in Google user.');\n }\n const tokenSource = new UserTokenSource(principal.email, google.scope, {\n sub: principal.sub,\n accessToken: google.accessToken,\n expiresAt: google.expiresAt,\n refreshToken: google.refreshToken,\n cache: userTokens,\n now: () => now().getTime(),\n refresh: async (refreshToken) => {\n const fresh = await refreshGoogleToken(oauth, {\n refreshToken,\n fetch: opts.fetch ?? ((input, init) => fetch(input, init)),\n nowSeconds: Math.floor(now().getTime() / 1000),\n });\n return { accessToken: fresh.accessToken, expiresAt: fresh.expiresAt };\n },\n });\n return sessionWith(\n new GscClient({\n tokenSource,\n logger,\n fetch: opts.fetch,\n cache,\n cacheTtlMs: config.cacheTtlSeconds * 1000,\n }),\n principal,\n );\n },\n };\n}\n","import { createMcpHandler, type McpHttpHandler } from '@modelcontextprotocol/server';\nimport { clientLabel } from './activity.js';\nimport { loadConfig, type ConfigIssue, type Env } from './config.js';\nimport { timingSafeEqual } from './crypto.js';\nimport { createDashboardApi } from './dashboard-api.js';\nimport { createOAuthServer } from './oauth/server.js';\nimport { OWNER, type Principal } from './principal.js';\nimport { createSealer } from './seal.js';\nimport { DASHBOARD_HEADERS, dashboardShell } from './dashboard-shell.js';\nimport { createLogger, type Logger, type LogWriter } from './log.js';\nimport { buildMcpServer } from './mcp.js';\nimport { HTML_SECURITY_HEADERS, runningPage, setupPage } from './pages.js';\nimport { RateLimiter } from './ratelimit.js';\nimport { createRuntime, type Runtime } from './runtime.js';\nimport { VERSION } from './version.js';\n\nexport interface PlatformOptions {\n name: 'node' | 'vercel' | 'netlify' | 'cloudflare' | 'test';\n /** Outbound fetch for Google APIs (tests inject a mock). */\n fetch?: typeof fetch;\n logWriter?: LogWriter;\n /** Server is bound to loopback: enforce Host-header checks and allow localhost origins. */\n localOnly?: boolean;\n now?: () => Date;\n /**\n * Serves built static files (dashboard assets) for adapters without a platform CDN layer.\n * Returns null when no file matches.\n */\n serveStatic?: (request: Request) => Promise<Response | null>;\n}\n\nexport interface App {\n fetch(request: Request): Promise<Response>;\n /** Configuration problems; non-empty means the app only serves the setup page. */\n readonly setupErrors: ConfigIssue[];\n readonly warnings: string[];\n readonly runtime: Runtime | null;\n}\n\nconst MAX_BODY_BYTES = 1_000_000;\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);\nconst MCP_CORS_HEADERS = {\n 'access-control-allow-methods': 'POST, GET, DELETE, OPTIONS',\n 'access-control-allow-headers':\n 'authorization, content-type, accept, mcp-protocol-version, mcp-method, mcp-name, mcp-session-id, last-event-id',\n 'access-control-expose-headers': 'www-authenticate, mcp-protocol-version, mcp-session-id',\n 'access-control-max-age': '600',\n};\n\nfunction json(body: unknown, status = 200, headers: Record<string, string> = {}): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json', 'cache-control': 'no-store', ...headers },\n });\n}\n\nfunction html(body: string, status = 200): Response {\n return new Response(body, { status, headers: HTML_SECURITY_HEADERS });\n}\n\n/** Malformed percent-encoding must look like any other wrong secret (404), not a 500. */\nfunction safeDecode(segment: string): string {\n try {\n return decodeURIComponent(segment);\n } catch {\n return segment;\n }\n}\n\n/** Deployments are private endpoints; ask crawlers not to index them. */\nconst robots = () =>\n new Response('User-agent: *\\nDisallow: /\\n', {\n headers: {\n 'content-type': 'text/plain; charset=utf-8',\n 'cache-control': 'public, max-age=86400',\n },\n });\n\nconst notFound = () =>\n new Response('Not found', { status: 404, headers: { 'content-type': 'text/plain' } });\n\nfunction hostnameOf(hostHeader: string | null): string | null {\n if (!hostHeader) return null;\n try {\n return new URL(`http://${hostHeader}`).hostname;\n } catch {\n return null;\n }\n}\n\n/** Builds the web-standard request handler shared by every adapter. */\nexport function createApp(env: Env, platform: PlatformOptions): App {\n const loaded = loadConfig(env, { transport: 'http' });\n if (!loaded.ok) {\n const logger = createLogger({ write: platform.logWriter, base: { platform: platform.name } });\n logger.error('configuration invalid; serving setup page only', {\n issues: loaded.errors.map((e) => `${e.variable ?? ''} ${e.message}`.trim()),\n });\n return {\n setupErrors: loaded.errors,\n warnings: [],\n runtime: null,\n async fetch(request) {\n const path = new URL(request.url).pathname;\n if (path === '/healthz') return json({ status: 'setup_required', version: VERSION }, 503);\n if (path === '/robots.txt') return robots();\n if (path === '/' && (request.method === 'GET' || request.method === 'HEAD')) {\n return html(setupPage(loaded.errors), 503);\n }\n return json(\n {\n error: 'setup_required',\n error_description: 'This GSClaw deployment is not configured.',\n },\n 503,\n );\n },\n };\n }\n\n const { config } = loaded;\n const runtime = createRuntime(config, {\n fetch: platform.fetch,\n logWriter: platform.logWriter,\n now: platform.now,\n logFields: { platform: platform.name },\n });\n const { logger } = runtime;\n for (const warning of loaded.warnings) logger.warn(warning);\n\n const now = platform.now ?? (() => new Date());\n const oauth = createOAuthServer({\n config,\n sealer: createSealer(config.sealingSecret!, () => now().getTime()),\n logger,\n fetch: platform.fetch ?? ((input, init) => fetch(input, init)),\n now,\n });\n const limiter = new RateLimiter(config.rateLimitPerMinute);\n const publicOrigin = config.publicBaseUrl ? new URL(config.publicBaseUrl).origin : null;\n\n /**\n * Public base URL for OAuth metadata and token audiences: PUBLIC_BASE_URL (or the platform's\n * detected URL), else the forwarded or request origin.\n */\n const baseUrl = (request: Request, url: URL): string => {\n if (config.publicBaseUrl) return config.publicBaseUrl;\n const proto = request.headers.get('x-forwarded-proto')?.split(',')[0]?.trim();\n const host = request.headers.get('x-forwarded-host')?.split(',')[0]?.trim();\n if (proto && host && /^https?$/.test(proto)) return `${proto}://${host}`;\n return url.origin;\n };\n\n const dashboardApi = config.dashboard\n ? createDashboardApi({\n config,\n runtime,\n oauth,\n logger,\n platform: platform.name,\n fetch: platform.fetch,\n now: platform.now,\n baseUrl,\n })\n : null;\n\n const mcp: McpHttpHandler = createMcpHandler(\n ({ authInfo }) => {\n const principal = authInfo?.extra?.principal as Principal;\n const session = runtime.sessionFor(principal);\n return buildMcpServer({\n tools: session.tools,\n ctx: session.ctx,\n logger,\n activity: runtime.activity,\n client: typeof authInfo?.extra?.client === 'string' ? authInfo.extra.client : 'unknown',\n user: principal.kind === 'user' ? principal.email : undefined,\n });\n },\n {\n legacy: 'stateless',\n responseMode: 'auto',\n onerror: (error) => logger.debug('mcp transport error', { error }),\n },\n );\n\n const originAllowed = (origin: string, requestUrl: URL): boolean => {\n if (origin === requestUrl.origin || origin === publicOrigin) return true;\n if (config.allowedOrigins.includes(origin)) return true;\n if (platform.localOnly) {\n const host = hostnameOf(origin.replace(/^https?:\\/\\//, ''));\n return host !== null && LOOPBACK_HOSTS.has(host);\n }\n return false;\n };\n\n const corsHeaders = (origin: string | null): Record<string, string> =>\n origin ? { 'access-control-allow-origin': origin, vary: 'Origin', ...MCP_CORS_HEADERS } : {};\n\n /**\n * Resolves the caller of /mcp. Service-account mode: the static access token, the secret path, or\n * a GSClaw-issued OAuth token (owner sign-in). OAuth mode: a GSClaw-issued token for a Google user.\n */\n async function authenticate(\n request: Request,\n path: string,\n base: string,\n ): Promise<Principal | Response> {\n if (path !== '/mcp') {\n const secret = path.slice('/mcp/'.length);\n // Wrong secret paths are indistinguishable from any other unknown URL.\n if (\n config.authMode !== 'service_account' ||\n !config.secretPath ||\n secret.includes('/') ||\n !(await timingSafeEqual(safeDecode(secret), config.accessToken!))\n ) {\n return notFound();\n }\n return { ...OWNER, via: 'secret_path' };\n }\n\n const header = request.headers.get('authorization') ?? '';\n const token = /^Bearer\\s+(.+)$/i.exec(header)?.[1]?.trim();\n if (token) {\n if (\n config.authMode === 'service_account' &&\n (await timingSafeEqual(token, config.accessToken!))\n ) {\n return OWNER;\n }\n const principal = await oauth.verifyAccessToken(token, base);\n if (principal) return principal;\n }\n const hint =\n config.authMode === 'oauth'\n ? 'Sign in with OAuth (your MCP client does this automatically).'\n : \"Send 'Authorization: Bearer <GSCLAW_ACCESS_TOKEN>' or sign in with OAuth.\";\n return json(\n { error: 'invalid_token', error_description: `Missing or invalid credentials. ${hint}` },\n 401,\n {\n 'www-authenticate': oauth.challenge(base, token ? 'invalid_token' : undefined),\n },\n );\n }\n\n async function handleMcp(\n request: Request,\n url: URL,\n path: string,\n log: Logger,\n ): Promise<Response> {\n const origin = request.headers.get('origin');\n if (origin && !originAllowed(origin, url)) {\n log.warn('rejected request from disallowed origin', { origin });\n return json(\n {\n error: 'forbidden',\n error_description: 'Origin not allowed. Add it to GSCLAW_ALLOWED_ORIGINS.',\n },\n 403,\n );\n }\n if (platform.localOnly) {\n const host = hostnameOf(request.headers.get('host'));\n if (!host || !LOOPBACK_HOSTS.has(host)) {\n return json({ error: 'forbidden', error_description: 'Invalid Host header.' }, 403);\n }\n }\n if (request.method === 'OPTIONS')\n return new Response(null, { status: 204, headers: corsHeaders(origin) });\n\n const principal = await authenticate(request, path, baseUrl(request, url));\n if (principal instanceof Response) {\n for (const [k, v] of Object.entries(corsHeaders(origin))) principal.headers.set(k, v);\n return principal;\n }\n\n const limit = limiter.check(principal.sub);\n if (!limit.ok) {\n return json(\n { error: 'rate_limited', error_description: 'Too many requests. Slow down and retry.' },\n 429,\n {\n 'retry-after': String(limit.retryAfterSeconds),\n ...corsHeaders(origin),\n },\n );\n }\n\n const length = Number(request.headers.get('content-length') ?? 0);\n if (length > MAX_BODY_BYTES) return json({ error: 'payload_too_large' }, 413);\n const body = request.method === 'POST' ? await request.text() : undefined;\n if (body && body.length > MAX_BODY_BYTES) return json({ error: 'payload_too_large' }, 413);\n\n // Hand the SDK a clean request: canonical URL, no credentials.\n const headers = new Headers(request.headers);\n headers.delete('authorization');\n headers.delete('cookie');\n const clean = new Request(new URL('/mcp', url), { method: request.method, headers, body });\n\n const response = await mcp.fetch(clean, {\n authInfo: {\n token: 'redacted',\n clientId: principal.clientId ?? principal.sub,\n scopes: principal.scopes,\n extra: {\n principal,\n client: principal.clientName ?? clientLabel(request.headers.get('user-agent')),\n },\n },\n });\n const out = new Response(response.body, response);\n out.headers.set('cache-control', 'no-store');\n for (const [k, v] of Object.entries(corsHeaders(origin))) out.headers.set(k, v);\n return out;\n }\n\n async function route(request: Request, log: Logger): Promise<Response> {\n const url = new URL(request.url);\n const path = url.pathname.length > 1 ? url.pathname.replace(/\\/+$/, '') : url.pathname;\n\n if (path === '/healthz') return json({ status: 'ok', version: VERSION });\n if (path === '/robots.txt') return robots();\n if (path === '/mcp' || path.startsWith('/mcp/')) return handleMcp(request, url, path, log);\n const oauthResponse = await oauth.handle(request, url, path, baseUrl(request, url));\n if (oauthResponse) return oauthResponse;\n const isRead = request.method === 'GET' || request.method === 'HEAD';\n if (path === '/' && isRead) {\n return dashboardApi\n ? new Response(dashboardShell(), { headers: DASHBOARD_HEADERS })\n : html(runningPage());\n }\n if (path === '/api' || path.startsWith('/api/')) {\n return dashboardApi ? dashboardApi.handle(request, url, path) : notFound();\n }\n if (isRead && platform.serveStatic) {\n const file = await platform.serveStatic(request);\n if (file) return file;\n }\n return notFound();\n }\n\n return {\n setupErrors: [],\n warnings: loaded.warnings,\n runtime,\n async fetch(request) {\n const started = Date.now();\n const url = new URL(request.url);\n // Never log the secret path segment or OAuth query strings.\n const logPath = url.pathname.startsWith('/mcp/') ? '/mcp/<secret>' : url.pathname;\n const log = logger.child({ method: request.method, path: logPath });\n let response: Response;\n try {\n response = await route(request, log);\n } catch (error) {\n log.error('unhandled error', { error });\n response = json({ error: 'internal_error' }, 500);\n }\n if (logPath !== '/healthz')\n log.info('request', { status: response.status, ms: Date.now() - started });\n return response;\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-DhK9JWEH.js","names":["GOOGLE_TOKEN_URL","pkg.version","LOOPBACK","json","html","json"],"sources":["../src/core/activity.ts","../src/core/crypto.ts","../src/core/log.ts","../src/core/config.ts","../src/core/errors.ts","../src/core/gsc/sites.ts","../src/core/gsc/auth.ts","../package.json","../src/core/version.ts","../src/core/pages.ts","../src/core/principal.ts","../src/core/ratelimit.ts","../src/core/session.ts","../src/core/oauth/clients.ts","../src/core/oauth/google.ts","../src/core/oauth/pages.ts","../src/core/oauth/server.ts","../src/core/update-check.ts","../src/core/dashboard-api.ts","../src/core/seal.ts","../src/core/dashboard-shell.ts","../src/core/format.ts","../src/core/prompts.ts","../src/core/mcp.ts","../src/core/cache.ts","../src/core/gsc/client.ts","../src/core/analysis/compare.ts","../src/core/dates.ts","../src/core/gsc/types.ts","../src/core/tools/common.ts","../src/core/tools/types.ts","../src/core/tools/compare.ts","../src/core/analysis/decay.ts","../src/core/tools/content-decay.ts","../src/core/tools/inspection.ts","../src/core/analysis/ctr-curve.ts","../src/core/analysis/opportunities.ts","../src/core/tools/opportunities.ts","../src/core/tools/page-report.ts","../src/core/tools/search-analytics.ts","../src/core/tools/sites.ts","../src/core/tools/sitemaps.ts","../src/core/tools/registry.ts","../src/core/runtime.ts","../src/core/app.ts"],"sourcesContent":["export interface ActivityEntry {\n time: string;\n /** Best-effort client name (from the User-Agent or transport). */\n client: string;\n tool: string;\n site: string | null;\n ms: number;\n ok: boolean;\n error_kind?: string;\n /** Signed-in Google user (OAuth mode); used to show each person only their own calls. */\n user?: string;\n}\n\n/**\n * Recent tool calls for the dashboard's trust view: metadata only, never arguments beyond the\n * property or response payloads. In-memory and per instance: serverless platforms may run several\n * instances, and each keeps its own list.\n */\nexport class ActivityLog {\n readonly persistent = false;\n private readonly entries: ActivityEntry[] = [];\n\n constructor(private readonly capacity = 200) {}\n\n record(entry: ActivityEntry): void {\n this.entries.push(entry);\n if (this.entries.length > this.capacity) this.entries.shift();\n }\n\n list(limit = this.capacity): ActivityEntry[] {\n return this.entries.slice(-limit).reverse();\n }\n}\n\nconst KNOWN_CLIENTS: [RegExp, string][] = [\n [/claude-code|claude code/i, 'Claude Code'],\n [/claude/i, 'Claude'],\n [/chatgpt|openai/i, 'ChatGPT'],\n [/cursor/i, 'Cursor'],\n [/visual studio code|vscode|copilot/i, 'VS Code'],\n [/inspector/i, 'MCP Inspector'],\n [/openclaw/i, 'OpenClaw'],\n [/gemini/i, 'Gemini CLI'],\n [/codex/i, 'Codex'],\n];\n\n/** Maps a User-Agent to a short client label. */\nexport function clientLabel(userAgent: string | null): string {\n if (!userAgent) return 'unknown';\n for (const [pattern, label] of KNOWN_CLIENTS) if (pattern.test(userAgent)) return label;\n return userAgent.split(/[\\s/]/)[0]!.slice(0, 40) || 'unknown';\n}\n","// Runtime-agnostic crypto helpers built on WebCrypto, atob/btoa and TextEncoder only.\n\nconst encoder = new TextEncoder();\n\nexport function utf8(input: string): Uint8Array<ArrayBuffer> {\n return encoder.encode(input);\n}\n\nexport function bytesToBase64(bytes: Uint8Array): string {\n let binary = '';\n for (let i = 0; i < bytes.length; i += 0x8000) {\n binary += String.fromCharCode(...bytes.subarray(i, i + 0x8000));\n }\n return btoa(binary);\n}\n\nexport function base64ToBytes(base64: string): Uint8Array<ArrayBuffer> {\n const normalized = base64.replace(/-/g, '+').replace(/_/g, '/').replace(/\\s+/g, '');\n const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4);\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\nexport function base64UrlEncode(input: Uint8Array | string): string {\n const bytes = typeof input === 'string' ? utf8(input) : input;\n return bytesToBase64(bytes).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nexport async function sha256(input: string): Promise<Uint8Array> {\n return new Uint8Array(await crypto.subtle.digest('SHA-256', utf8(input)));\n}\n\nexport async function sha256Hex(input: string): Promise<string> {\n return [...(await sha256(input))].map((b) => b.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Constant-time string comparison. Both inputs are hashed first so the comparison time does not\n * depend on where the strings differ or on their lengths.\n */\nexport async function timingSafeEqual(a: string, b: string): Promise<boolean> {\n const [ha, hb] = await Promise.all([sha256(a), sha256(b)]);\n let diff = 0;\n for (let i = 0; i < ha.length; i++) diff |= ha[i]! ^ hb[i]!;\n return diff === 0;\n}\n\nexport function randomToken(bytes = 32): string {\n const buf = crypto.getRandomValues(new Uint8Array(bytes));\n return [...buf].map((b) => b.toString(16).padStart(2, '0')).join('');\n}\n","export const LOG_LEVELS = ['debug', 'info', 'warn', 'error', 'silent'] as const;\nexport type LogLevel = (typeof LOG_LEVELS)[number];\n\ntype Fields = Record<string, unknown>;\n\nexport interface Logger {\n debug(msg: string, fields?: Fields): void;\n info(msg: string, fields?: Fields): void;\n warn(msg: string, fields?: Fields): void;\n error(msg: string, fields?: Fields): void;\n child(fields: Fields): Logger;\n}\n\nexport type LogWriter = (line: string, level: Exclude<LogLevel, 'silent'>) => void;\n\nconst RANK: Record<LogLevel, number> = { debug: 10, info: 20, warn: 30, error: 40, silent: 100 };\n\n// Anything whose key looks like a credential is replaced before it reaches the log line.\nconst SECRET_KEY =\n /token|secret|password|authorization|cookie|private_?key|assertion|credential|api_?key/i;\n\nexport const defaultWriter: LogWriter = (line, level) => {\n if (level === 'error' || level === 'warn') console.error(line);\n else console.log(line);\n};\n\n/** For stdio transports: stdout carries the MCP protocol, so every log line goes to stderr. */\nexport const stderrWriter: LogWriter = (line) => console.error(line);\n\nexport function redact(value: unknown, depth = 0): unknown {\n if (value instanceof Error) return { name: value.name, message: value.message };\n if (depth > 4 || value === null || typeof value !== 'object') return value;\n if (Array.isArray(value)) return value.slice(0, 20).map((v) => redact(v, depth + 1));\n const out: Fields = {};\n for (const [key, v] of Object.entries(value)) {\n out[key] = SECRET_KEY.test(key) ? '[redacted]' : redact(v, depth + 1);\n }\n return out;\n}\n\nexport function createLogger(\n options: { level?: LogLevel; write?: LogWriter; base?: Fields } = {},\n): Logger {\n const threshold = RANK[options.level ?? 'info'];\n const write = options.write ?? defaultWriter;\n const base = options.base ?? {};\n\n const emit = (level: Exclude<LogLevel, 'silent'>, msg: string, fields?: Fields) => {\n if (RANK[level] < threshold) return;\n const entry = { time: new Date().toISOString(), level, msg, ...base, ...(fields ?? {}) };\n write(JSON.stringify(redact(entry)), level);\n };\n\n return {\n debug: (msg, fields) => emit('debug', msg, fields),\n info: (msg, fields) => emit('info', msg, fields),\n warn: (msg, fields) => emit('warn', msg, fields),\n error: (msg, fields) => emit('error', msg, fields),\n child: (fields) => createLogger({ level: options.level, write, base: { ...base, ...fields } }),\n };\n}\n\nexport const silentLogger: Logger = createLogger({ level: 'silent' });\n","import { base64ToBytes } from './crypto.js';\nimport { LOG_LEVELS, type LogLevel } from './log.js';\n\nexport type Env = Record<string, string | undefined>;\nexport type Transport = 'http' | 'stdio';\nexport type AuthMode = 'service_account' | 'oauth';\n\nexport interface ServiceAccountKey {\n client_email: string;\n private_key: string;\n private_key_id?: string;\n project_id?: string;\n token_uri?: string;\n}\n\nexport interface GoogleOAuthConfig {\n clientId: string;\n clientSecret: string;\n /** Lower-cased emails allowed to sign in (empty = no email restriction). */\n allowedEmails: string[];\n /** Lower-cased email domains allowed to sign in (empty = no domain restriction). */\n allowedDomains: string[];\n}\n\nexport interface Config {\n authMode: AuthMode;\n serviceAccount?: ServiceAccountKey;\n /** Shared secret clients present in service-account mode (bearer header or secret path). */\n accessToken?: string;\n /** Google OAuth (multi-user) mode settings. */\n googleOAuth?: GoogleOAuthConfig;\n /**\n * Seals OAuth tokens, codes and dashboard sessions: GSCLAW_ENCRYPTION_KEY and, in service-account\n * mode, the access token. Rotating either revokes everything issued with it. Absent for stdio.\n */\n sealingSecret?: string;\n /** Extra hosts allowed as OAuth redirect targets (besides the built-in client list). */\n oauthRedirectHosts: string[];\n publicBaseUrl?: string;\n allowWrites: boolean;\n dashboard: boolean;\n secretPath: boolean;\n allowedOrigins: string[];\n logLevel: LogLevel;\n rateLimitPerMinute: number;\n cacheTtlSeconds: number;\n maxRows: number;\n batchInspectMax: number;\n}\n\nexport interface ConfigIssue {\n variable?: string;\n message: string;\n}\n\nexport type ConfigResult =\n { ok: true; config: Config; warnings: string[] } | { ok: false; errors: ConfigIssue[] };\n\nexport const MIN_ACCESS_TOKEN_LENGTH = 32;\nexport const SETUP_DOCS_URL = 'https://github.com/tuyakhov/gsclaw/blob/main/docs/setup.md';\n\n/**\n * Loads and validates configuration from environment variables. Never throws.\n *\n * Error messages name variables but never echo their values: on serverless deployments they are\n * shown on the public setup page, and a secret pasted into the wrong variable must not leak.\n */\nexport function loadConfig(env: Env, opts: { transport: Transport }): ConfigResult {\n const errors: ConfigIssue[] = [];\n const warnings: string[] = [];\n const read = (key: string) => {\n const value = env[key]?.trim();\n return value ? value : undefined;\n };\n\n const hasServiceAccount = Boolean(read('GOOGLE_SERVICE_ACCOUNT_JSON'));\n const hasOAuth = Boolean(read('GOOGLE_OAUTH_CLIENT_ID') || read('GOOGLE_OAUTH_CLIENT_SECRET'));\n const explicitMode = read('GSCLAW_AUTH_MODE');\n\n let authMode: AuthMode | undefined;\n if (explicitMode) {\n if (explicitMode === 'service_account' || explicitMode === 'oauth') authMode = explicitMode;\n else\n errors.push({\n variable: 'GSCLAW_AUTH_MODE',\n message: 'must be \"service_account\" or \"oauth\".',\n });\n } else if (hasServiceAccount && hasOAuth) {\n errors.push({\n variable: 'GSCLAW_AUTH_MODE',\n message:\n 'both service-account and Google OAuth variables are set. Set GSCLAW_AUTH_MODE to \"service_account\" or \"oauth\" to choose one.',\n });\n } else if (hasServiceAccount) authMode = 'service_account';\n else if (hasOAuth) authMode = 'oauth';\n else {\n errors.push({\n message:\n 'No authentication mode is configured, so GSClaw refuses to start (it never runs open). ' +\n (opts.transport === 'http'\n ? 'Set GOOGLE_SERVICE_ACCOUNT_JSON and GSCLAW_ACCESS_TOKEN (service-account mode), or GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET, GSCLAW_ENCRYPTION_KEY and PUBLIC_BASE_URL (Google OAuth mode).'\n : 'Set GOOGLE_SERVICE_ACCOUNT_JSON or GOOGLE_APPLICATION_CREDENTIALS.') +\n ` See ${SETUP_DOCS_URL}`,\n });\n }\n\n let serviceAccount: ServiceAccountKey | undefined;\n let accessToken: string | undefined;\n\n if (authMode === 'service_account') {\n const raw = read('GOOGLE_SERVICE_ACCOUNT_JSON');\n if (!raw) {\n errors.push({\n variable: 'GOOGLE_SERVICE_ACCOUNT_JSON',\n message: 'is required in service-account mode (paste the key JSON, raw or base64).',\n });\n } else {\n const parsed = parseServiceAccountJson(raw);\n if ('error' in parsed)\n errors.push({ variable: 'GOOGLE_SERVICE_ACCOUNT_JSON', message: parsed.error });\n else serviceAccount = parsed.key;\n }\n\n if (opts.transport === 'http') {\n accessToken = read('GSCLAW_ACCESS_TOKEN');\n if (!accessToken) {\n errors.push({\n variable: 'GSCLAW_ACCESS_TOKEN',\n message:\n 'is required for HTTP deployments so the endpoint is never public. Generate one with `openssl rand -hex 32` or `npx gsclaw generate-token`.',\n });\n } else if (accessToken.length < MIN_ACCESS_TOKEN_LENGTH) {\n errors.push({\n variable: 'GSCLAW_ACCESS_TOKEN',\n message: `must be at least ${MIN_ACCESS_TOKEN_LENGTH} characters. Generate one with \\`openssl rand -hex 32\\`.`,\n });\n }\n }\n }\n\n const encryptionKey = read('GSCLAW_ENCRYPTION_KEY');\n if (encryptionKey && encryptionKey.length < MIN_ACCESS_TOKEN_LENGTH) {\n errors.push({\n variable: 'GSCLAW_ENCRYPTION_KEY',\n message: `must be at least ${MIN_ACCESS_TOKEN_LENGTH} characters. Generate one with \\`openssl rand -hex 32\\`.`,\n });\n }\n\n let googleOAuth: GoogleOAuthConfig | undefined;\n if (authMode === 'oauth') {\n if (opts.transport === 'stdio') {\n errors.push({\n variable: 'GSCLAW_AUTH_MODE',\n message:\n 'Google OAuth mode is for HTTP deployments. For local stdio use, set GOOGLE_SERVICE_ACCOUNT_JSON or GOOGLE_APPLICATION_CREDENTIALS instead.',\n });\n }\n const clientId = read('GOOGLE_OAUTH_CLIENT_ID');\n const clientSecret = read('GOOGLE_OAUTH_CLIENT_SECRET');\n if (!clientId) {\n errors.push({\n variable: 'GOOGLE_OAUTH_CLIENT_ID',\n message:\n 'is required in OAuth mode (Google Cloud → APIs & Services → Credentials → OAuth client ID).',\n });\n }\n if (!clientSecret) {\n errors.push({\n variable: 'GOOGLE_OAUTH_CLIENT_SECRET',\n message: 'is required in OAuth mode.',\n });\n }\n if (!encryptionKey) {\n errors.push({\n variable: 'GSCLAW_ENCRYPTION_KEY',\n message:\n 'is required in OAuth mode; it encrypts the tokens GSClaw issues. Generate one with `openssl rand -hex 32`.',\n });\n }\n const list = (key: string) =>\n (read(key) ?? '')\n .split(',')\n .map((v) => v.trim().toLowerCase().replace(/^@/, ''))\n .filter(Boolean);\n if (clientId && clientSecret) {\n googleOAuth = {\n clientId,\n clientSecret,\n allowedEmails: list('ALLOWED_GOOGLE_EMAILS'),\n allowedDomains: list('ALLOWED_GOOGLE_DOMAINS'),\n };\n }\n }\n\n const bool = (key: string, fallback: boolean) => {\n const value = read(key);\n if (value === undefined) return fallback;\n const v = value.toLowerCase();\n if (['1', 'true', 'yes', 'on'].includes(v)) return true;\n if (['0', 'false', 'no', 'off'].includes(v)) return false;\n errors.push({ variable: key, message: 'must be \"true\" or \"false\".' });\n return fallback;\n };\n\n const int = (key: string, fallback: number, min: number, max: number) => {\n const value = read(key);\n if (value === undefined) return fallback;\n const n = Number(value);\n if (!Number.isInteger(n) || n < min || n > max) {\n errors.push({\n variable: key,\n message: `must be an integer between ${min} and ${max}.`,\n });\n return fallback;\n }\n return n;\n };\n\n const logLevelRaw = (read('LOG_LEVEL') ?? 'info').toLowerCase();\n let logLevel: LogLevel = 'info';\n if ((LOG_LEVELS as readonly string[]).includes(logLevelRaw)) logLevel = logLevelRaw as LogLevel;\n else errors.push({ variable: 'LOG_LEVEL', message: `must be one of ${LOG_LEVELS.join(', ')}.` });\n\n let publicBaseUrl = read('PUBLIC_BASE_URL') ?? detectPlatformBaseUrl(env);\n if (publicBaseUrl) {\n try {\n const url = new URL(publicBaseUrl);\n publicBaseUrl = url.origin + url.pathname.replace(/\\/+$/, '');\n } catch {\n errors.push({\n variable: 'PUBLIC_BASE_URL',\n message: 'is not a valid URL (expected e.g. https://gsclaw.example.com).',\n });\n publicBaseUrl = undefined;\n }\n }\n\n const allowedOrigins: string[] = [];\n for (const entry of (read('GSCLAW_ALLOWED_ORIGINS') ?? '').split(',')) {\n const value = entry.trim();\n if (!value) continue;\n try {\n allowedOrigins.push(new URL(value).origin);\n } catch {\n errors.push({\n variable: 'GSCLAW_ALLOWED_ORIGINS',\n message: 'contains an invalid origin (expected e.g. https://app.example.com).',\n });\n }\n }\n\n const allowWrites = bool('GSCLAW_ALLOW_WRITES', false);\n const dashboard = bool('GSCLAW_DASHBOARD', true);\n const secretPath = bool('GSCLAW_SECRET_PATH', true);\n const rateLimitPerMinute = int('GSCLAW_RATE_LIMIT_PER_MINUTE', 120, 0, 100_000);\n const cacheTtlSeconds = int('GSCLAW_CACHE_TTL_SECONDS', 300, 0, 86_400);\n const maxRows = int('GSCLAW_MAX_ROWS', 25_000, 1, 500_000);\n const batchInspectMax = int('GSCLAW_BATCH_INSPECT_MAX', 50, 1, 500);\n\n const oauthRedirectHosts = (read('GSCLAW_OAUTH_REDIRECT_HOSTS') ?? '')\n .split(',')\n .map((h) => h.trim().toLowerCase())\n .filter(Boolean);\n\n if (authMode === 'oauth' && opts.transport === 'http') {\n if (!publicBaseUrl) {\n errors.push({\n variable: 'PUBLIC_BASE_URL',\n message:\n 'is required in OAuth mode (e.g. https://gsclaw.example.com); Google redirects back to PUBLIC_BASE_URL/oauth/google/callback.',\n });\n } else {\n const url = new URL(publicBaseUrl);\n const loopback = ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname);\n if (url.protocol !== 'https:' && !loopback) {\n errors.push({ variable: 'PUBLIC_BASE_URL', message: 'must use https in OAuth mode.' });\n }\n }\n }\n\n if (errors.length > 0 || !authMode) return { ok: false, errors };\n\n if (\n googleOAuth &&\n googleOAuth.allowedEmails.length === 0 &&\n googleOAuth.allowedDomains.length === 0\n ) {\n warnings.push(\n 'No ALLOWED_GOOGLE_EMAILS or ALLOWED_GOOGLE_DOMAINS set: anyone with a Google account can sign in to this deployment (each person only sees their own Search Console properties).',\n );\n }\n\n if (allowWrites) {\n warnings.push(\n 'GSCLAW_ALLOW_WRITES=true: submit_sitemap and delete_sitemap are enabled. The service account needs Full permission on each property.',\n );\n }\n\n return {\n ok: true,\n warnings,\n config: {\n authMode,\n serviceAccount,\n accessToken,\n googleOAuth,\n // In service-account mode the access token is always part of the secret, so rotating it\n // revokes every OAuth token and dashboard session issued with the old one.\n sealingSecret:\n opts.transport !== 'http'\n ? undefined\n : accessToken\n ? [encryptionKey, accessToken].filter(Boolean).join('\\n')\n : encryptionKey,\n oauthRedirectHosts,\n publicBaseUrl,\n allowWrites,\n dashboard,\n secretPath,\n allowedOrigins,\n logLevel,\n rateLimitPerMinute,\n cacheTtlSeconds,\n maxRows,\n batchInspectMax,\n },\n };\n}\n\nexport function parseServiceAccountJson(\n raw: string,\n): { key: ServiceAccountKey } | { error: string } {\n let text = raw.trim();\n if (!text.startsWith('{')) {\n try {\n text = new TextDecoder().decode(base64ToBytes(text)).trim();\n } catch {\n return { error: 'is neither JSON nor base64-encoded JSON.' };\n }\n }\n let data: unknown;\n try {\n data = JSON.parse(text);\n } catch {\n return {\n error:\n 'is not valid JSON. Paste the whole key file you downloaded from Google Cloud (or base64-encode it).',\n };\n }\n if (!data || typeof data !== 'object') return { error: 'must be a JSON object.' };\n const obj = data as Record<string, unknown>;\n if (obj.type !== undefined && obj.type !== 'service_account') {\n return {\n error: `has type \"${JSON.stringify(obj.type)}\" but must be a service-account key (\"type\": \"service_account\"). OAuth client JSON files belong in GOOGLE_OAUTH_CLIENT_ID/SECRET instead.`,\n };\n }\n if (typeof obj.client_email !== 'string' || !obj.client_email.includes('@')) {\n return { error: 'is missing \"client_email\".' };\n }\n if (typeof obj.private_key !== 'string' || !obj.private_key.includes('PRIVATE KEY')) {\n return { error: 'is missing \"private_key\".' };\n }\n // Env UIs often turn real newlines into literal \"\\n\" sequences.\n const privateKey = obj.private_key.includes('\\n')\n ? obj.private_key\n : obj.private_key.replace(/\\\\n/g, '\\n');\n\n return {\n key: {\n client_email: obj.client_email,\n private_key: privateKey,\n private_key_id: typeof obj.private_key_id === 'string' ? obj.private_key_id : undefined,\n project_id: typeof obj.project_id === 'string' ? obj.project_id : undefined,\n token_uri: typeof obj.token_uri === 'string' ? obj.token_uri : undefined,\n },\n };\n}\n\n/** Best-effort public URL detection so one-click deploys work without PUBLIC_BASE_URL. */\nexport function detectPlatformBaseUrl(env: Env): string | undefined {\n const https = (host?: string) =>\n host ? `https://${host.replace(/^https?:\\/\\//, '')}` : undefined;\n return (\n https(env.VERCEL_PROJECT_PRODUCTION_URL) ??\n env.URL ?? // Netlify\n env.RENDER_EXTERNAL_URL ??\n https(env.RAILWAY_PUBLIC_DOMAIN) ??\n env.APP_URL ?? // DigitalOcean App Platform\n (env.FLY_APP_NAME ? `https://${env.FLY_APP_NAME}.fly.dev` : undefined)\n );\n}\n\nexport function formatConfigErrors(errors: ConfigIssue[]): string {\n const lines = errors.map((e) => ` • ${e.variable ? `${e.variable} ` : ''}${e.message}`);\n return [\n 'GSClaw is not configured correctly and will not start:',\n '',\n ...lines,\n '',\n `Setup guide: ${SETUP_DOCS_URL}`,\n ].join('\\n');\n}\n","import type { GoogleErrorBody } from './gsc/types.js';\n\n/** An error whose message is written for the end user (and their AI) and safe to show verbatim. */\nexport class UserFacingError extends Error {\n readonly kind: string;\n constructor(message: string, kind = 'invalid_input') {\n super(message);\n this.name = 'UserFacingError';\n this.kind = kind;\n }\n}\n\nexport type GscErrorKind =\n | 'permission'\n | 'api_disabled'\n | 'not_found'\n | 'invalid_argument'\n | 'rate_limited'\n | 'quota_exceeded'\n | 'auth'\n | 'server'\n | 'network';\n\nexport interface Identity {\n kind: 'service_account' | 'user';\n email: string;\n projectId?: string;\n}\n\nexport class GscApiError extends UserFacingError {\n readonly status: number;\n readonly reason?: string;\n readonly googleMessage: string;\n readonly retryable: boolean;\n\n constructor(opts: {\n kind: GscErrorKind;\n status: number;\n advice: string;\n googleMessage: string;\n reason?: string;\n retryable?: boolean;\n }) {\n const suffix = opts.googleMessage ? `\\n\\nGoogle said: ${opts.googleMessage}` : '';\n super(`${opts.advice}${suffix}`, opts.kind);\n this.name = 'GscApiError';\n this.status = opts.status;\n this.reason = opts.reason;\n this.googleMessage = opts.googleMessage;\n this.retryable = opts.retryable ?? false;\n }\n}\n\nconst RATE_LIMIT_REASONS = new Set([\n 'rateLimitExceeded',\n 'userRateLimitExceeded',\n 'rateLimitExceededUnreg',\n]);\nconst DAILY_QUOTA_REASONS = new Set([\n 'quotaExceeded',\n 'dailyLimitExceeded',\n 'dailyLimitExceededUnreg',\n]);\n\nfunction who(identity: Identity): string {\n return identity.kind === 'service_account'\n ? `The service account ${identity.email}`\n : `Your Google account (${identity.email})`;\n}\n\n/** Turns a Google API error response into an actionable message. */\nexport function toGscApiError(\n status: number,\n body: unknown,\n ctx: { identity: Identity; siteUrl?: string },\n): GscApiError {\n const err = (body as GoogleErrorBody | undefined)?.error;\n const googleMessage = (err?.message ?? (typeof body === 'string' ? body : '')).slice(0, 500);\n const reasons = new Set<string>();\n for (const e of err?.errors ?? []) if (e.reason) reasons.add(e.reason);\n for (const d of err?.details ?? []) if (d.reason) reasons.add(d.reason);\n if (err?.status) reasons.add(err.status);\n const reason = [...reasons][0];\n const has = (...names: string[]) => names.some((n) => reasons.has(n));\n const site = ctx.siteUrl ? `'${ctx.siteUrl}'` : 'this property';\n const { identity } = ctx;\n\n if (\n status === 429 ||\n has(...RATE_LIMIT_REASONS) ||\n (has('RESOURCE_EXHAUSTED') && !has(...DAILY_QUOTA_REASONS))\n ) {\n return new GscApiError({\n kind: 'rate_limited',\n status,\n reason,\n googleMessage,\n retryable: true,\n advice:\n \"Google's Search Console API rate limit was hit (Search Analytics allows ~1,200 queries/min per site; URL Inspection 600/min). \" +\n 'Wait a minute and retry. If it keeps happening, use shorter date ranges or fewer page/query breakdowns, which count heavier against the load quota.',\n });\n }\n\n if (has(...DAILY_QUOTA_REASONS)) {\n return new GscApiError({\n kind: 'quota_exceeded',\n status,\n reason,\n googleMessage,\n advice:\n 'The daily Search Console API quota is used up (URL Inspection allows 2,000 inspections per property per day). It resets at midnight Pacific Time.',\n });\n }\n\n if (\n has('SERVICE_DISABLED', 'accessNotConfigured') ||\n /has not been used in project|is disabled/i.test(googleMessage)\n ) {\n const project = identity.projectId ?? googleMessage.match(/project (\\d+|[a-z][a-z0-9-]+)/)?.[1];\n const link = `https://console.cloud.google.com/apis/library/searchconsole.googleapis.com${project ? `?project=${project}` : ''}`;\n return new GscApiError({\n kind: 'api_disabled',\n status,\n reason,\n googleMessage,\n advice: `The Google Search Console API is not enabled for the Google Cloud project${project ? ` \"${project}\"` : ''}. Enable it at ${link}, wait a minute, then retry.`,\n });\n }\n\n if (status === 401) {\n return new GscApiError({\n kind: 'auth',\n status,\n reason,\n googleMessage,\n advice:\n identity.kind === 'service_account'\n ? 'Google rejected the service-account credentials. The key may have been deleted or rotated: create a new JSON key in Google Cloud and update GOOGLE_SERVICE_ACCOUNT_JSON.'\n : 'Your Google sign-in has expired or was revoked. Reconnect GSClaw in your AI client to sign in again.',\n });\n }\n\n if (status === 403) {\n const fix =\n identity.kind === 'service_account'\n ? `In Search Console, open the property → Settings → Users and permissions → Add user, and add ${identity.email} (Restricted is enough for reading; Full is needed for sitemap changes).`\n : 'Ask an owner of the property to add your Google account in Search Console → Settings → Users and permissions.';\n return new GscApiError({\n kind: 'permission',\n status,\n reason,\n googleMessage,\n advice:\n `${who(identity)} does not have access to ${site}. ${fix} ` +\n \"Also check the property format: domain properties look like 'sc-domain:example.com', URL-prefix properties like 'https://www.example.com/' (exact scheme, www and trailing slash). Run list_sites to see every property this server can read.\",\n });\n }\n\n if (status === 404) {\n return new GscApiError({\n kind: 'not_found',\n status,\n reason,\n googleMessage,\n advice: `Not found. Check the property and URL: run list_sites for valid properties, and pass sitemaps as full URLs exactly as list_sitemaps shows them.`,\n });\n }\n\n if (status === 400) {\n return new GscApiError({\n kind: 'invalid_argument',\n status,\n reason,\n googleMessage,\n advice:\n 'Google rejected the request parameters. Common causes: searchAppearance combined with other dimensions, aggregation_type \"byProperty\" with a page dimension/filter, Discover/Google News with query or position data, or dates older than 16 months.',\n });\n }\n\n return new GscApiError({\n kind: 'server',\n status,\n reason,\n googleMessage,\n retryable: status >= 500,\n advice: `Google's Search Console API returned an error (HTTP ${status}). This is usually temporary; try again shortly.`,\n });\n}\n\n/** Message for any thrown value, safe to return to clients. Unknown errors are not echoed in detail. */\nexport function publicErrorMessage(error: unknown): { message: string; kind: string } {\n if (error instanceof UserFacingError) return { message: error.message, kind: error.kind };\n return {\n message: 'Unexpected internal error. Check the server logs for details.',\n kind: 'internal',\n };\n}\n","import { UserFacingError } from '../errors.js';\nimport type { GscClient } from './client.js';\nimport type { SiteEntry } from './types.js';\n\nexport type PermissionLevel = 'owner' | 'full' | 'restricted' | 'unverified' | 'unknown';\n\nexport interface Property {\n site_url: string;\n type: 'domain' | 'url_prefix';\n /** Domain for `sc-domain:` properties, host for URL-prefix properties. */\n host: string;\n permission_level: PermissionLevel;\n /** False for unverified entries, which expose no data. */\n has_access: boolean;\n}\n\nexport function normalizePermission(level: string): PermissionLevel {\n switch (level.replace(/_/g, '').toLowerCase()) {\n case 'siteowner':\n return 'owner';\n case 'sitefulluser':\n return 'full';\n case 'siterestricteduser':\n return 'restricted';\n case 'siteunverifieduser':\n return 'unverified';\n default:\n return 'unknown';\n }\n}\n\nexport function toProperty(entry: SiteEntry): Property {\n const permission = normalizePermission(entry.permissionLevel);\n const isDomain = entry.siteUrl.startsWith('sc-domain:');\n let host = entry.siteUrl;\n if (isDomain) host = entry.siteUrl.slice('sc-domain:'.length);\n else {\n try {\n host = new URL(entry.siteUrl).host;\n } catch {\n // keep raw value\n }\n }\n return {\n site_url: entry.siteUrl,\n type: isDomain ? 'domain' : 'url_prefix',\n host: host.toLowerCase(),\n permission_level: permission,\n has_access: permission !== 'unverified',\n };\n}\n\nexport async function listProperties(\n client: GscClient,\n opts: { fresh?: boolean } = {},\n): Promise<Property[]> {\n const entries = await client.listSites(opts);\n return entries\n .map(toProperty)\n .sort(\n (a, b) =>\n Number(b.has_access) - Number(a.has_access) ||\n Number(b.type === 'domain') - Number(a.type === 'domain') ||\n a.site_url.localeCompare(b.site_url),\n );\n}\n\nfunction stripWww(host: string): string {\n return host.replace(/^www\\./, '');\n}\n\n/** True if `property` would contain data for `url`. */\nexport function propertyCoversUrl(property: Property, url: string): boolean {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return false;\n }\n const host = parsed.host.toLowerCase();\n if (property.type === 'domain') {\n return host === property.host || host.endsWith(`.${property.host}`);\n }\n return url.startsWith(property.site_url) || `${url}/` === property.site_url;\n}\n\nfunction describeAvailable(properties: Property[]): string {\n const usable = properties.filter((p) => p.has_access);\n if (usable.length === 0) {\n return 'This server cannot read any Search Console property yet. Add its Google identity as a user on your properties (Search Console → Settings → Users and permissions).';\n }\n const list = usable\n .slice(0, 20)\n .map((p) => `'${p.site_url}'`)\n .join(', ');\n return `Available properties: ${list}${usable.length > 20 ? `, … (${usable.length} total)` : ''}.`;\n}\n\n/**\n * Resolves user input ('sc-domain:example.com', 'https://www.example.com/', 'example.com',\n * or any URL on the site) to a property this identity can read.\n */\nexport async function resolveProperty(client: GscClient, input: string): Promise<Property> {\n const properties = await listProperties(client);\n const usable = properties.filter((p) => p.has_access);\n const raw = input.trim();\n const lower = raw.toLowerCase();\n\n const exact = properties.find(\n (p) => p.site_url.toLowerCase() === lower || p.site_url.toLowerCase() === `${lower}/`,\n );\n if (exact) {\n if (!exact.has_access) {\n throw new UserFacingError(\n `'${exact.site_url}' is listed but unverified for this identity, so Search Console exposes no data for it. Verify ownership or ask an owner for access. ${describeAvailable(properties)}`,\n 'permission',\n );\n }\n return exact;\n }\n\n if (lower.startsWith('sc-domain:')) {\n throw new UserFacingError(\n `No access to '${raw}'. ${describeAvailable(properties)}`,\n 'not_found',\n );\n }\n\n const hasScheme = /^https?:\\/\\//.test(lower);\n let url: URL;\n try {\n url = new URL(hasScheme ? raw : `https://${raw}`);\n } catch {\n throw new UserFacingError(\n `'${raw}' is not a valid property, domain or URL. ${describeAvailable(properties)}`,\n );\n }\n const host = url.host.toLowerCase();\n\n const domainMatch = usable\n .filter((p) => p.type === 'domain' && (host === p.host || host.endsWith(`.${p.host}`)))\n .sort((a, b) => b.host.length - a.host.length)[0];\n\n const prefixMatches = usable\n .filter((p) => p.type === 'url_prefix')\n .filter((p) => {\n if (hasScheme) return propertyCoversUrl(p, url.href);\n // Bare domains match any scheme and www/non-www variant of the same host.\n return (\n stripWww(p.host) === stripWww(host) &&\n (url.pathname === '/' || propertyCoversUrl(p, `https://${p.host}${url.pathname}`))\n );\n })\n .sort((a, b) => {\n if (b.site_url.length !== a.site_url.length && hasScheme)\n return b.site_url.length - a.site_url.length;\n // Prefer https and the exact host the user typed.\n const score = (p: Property) =>\n (p.site_url.startsWith('https://') ? 2 : 0) + (p.host === host ? 1 : 0);\n return score(b) - score(a);\n });\n\n // A full URL is most specific to its URL-prefix property; a bare domain is best served by the\n // domain property, which covers every host and protocol.\n const match = hasScheme ? (prefixMatches[0] ?? domainMatch) : (domainMatch ?? prefixMatches[0]);\n if (match) return match;\n\n throw new UserFacingError(\n `No Search Console property matches '${raw}'. ${describeAvailable(properties)}`,\n 'not_found',\n );\n}\n\n/** Resolves a page given as an absolute URL or a path ('/pricing') against a property. */\nexport function absolutePageUrl(property: Property, page: string): string | null {\n if (/^https?:\\/\\//i.test(page)) return page;\n if (!page.startsWith('/')) return null;\n if (property.type === 'url_prefix')\n return new URL(page.replace(/^\\//, ''), property.site_url).href;\n return null;\n}\n","import type { ServiceAccountKey } from '../config.js';\nimport { base64ToBytes, base64UrlEncode, utf8 } from '../crypto.js';\nimport { GscApiError, type Identity } from '../errors.js';\n\nexport const SCOPE_READONLY = 'https://www.googleapis.com/auth/webmasters.readonly';\nexport const SCOPE_READWRITE = 'https://www.googleapis.com/auth/webmasters';\nexport const GOOGLE_TOKEN_URL = 'https://oauth2.googleapis.com/token';\n\n/** Supplies Google access tokens for one identity (a service account or a signed-in user). */\nexport interface TokenSource {\n readonly identity: Identity;\n readonly scopes: string[];\n getAccessToken(): Promise<string>;\n /** Drops any cached token, e.g. after Google answered 401. */\n invalidate(): void;\n}\n\nconst EXPIRY_MARGIN_MS = 60_000;\n\n/** WebCrypto key type without depending on DOM or node:crypto typings. */\ntype SigningKey = Awaited<ReturnType<typeof crypto.subtle.importKey>>;\n\n/**\n * Service-account tokens via the OAuth 2.0 JWT bearer flow, signed with WebCrypto (RS256) so it\n * runs on Node and Cloudflare Workers alike. The imported key and the access token are cached for\n * the life of the instance.\n */\nexport class ServiceAccountTokenSource implements TokenSource {\n readonly identity: Identity;\n readonly scopes: string[];\n private readonly key: ServiceAccountKey;\n private readonly fetchImpl: typeof fetch;\n private readonly now: () => number;\n private cryptoKey?: Promise<SigningKey>;\n private cached?: { token: string; expiresAt: number };\n private inflight?: Promise<string>;\n\n constructor(\n key: ServiceAccountKey,\n scopes: string[],\n opts: { fetch?: typeof fetch; now?: () => number } = {},\n ) {\n this.key = key;\n this.scopes = scopes;\n this.fetchImpl = opts.fetch ?? ((input, init) => fetch(input, init));\n this.now = opts.now ?? Date.now;\n this.identity = { kind: 'service_account', email: key.client_email, projectId: key.project_id };\n }\n\n async getAccessToken(): Promise<string> {\n if (this.cached && this.cached.expiresAt - EXPIRY_MARGIN_MS > this.now())\n return this.cached.token;\n this.inflight ??= this.fetchToken().finally(() => {\n this.inflight = undefined;\n });\n return this.inflight;\n }\n\n invalidate(): void {\n this.cached = undefined;\n }\n\n /** Builds the signed JWT assertion. Exposed for tests. */\n async createAssertion(): Promise<string> {\n const iat = Math.floor(this.now() / 1000);\n const header = {\n alg: 'RS256',\n typ: 'JWT',\n ...(this.key.private_key_id ? { kid: this.key.private_key_id } : {}),\n };\n const claims = {\n iss: this.key.client_email,\n scope: this.scopes.join(' '),\n aud: this.key.token_uri ?? GOOGLE_TOKEN_URL,\n iat,\n exp: iat + 3600,\n };\n const signingInput = `${base64UrlEncode(JSON.stringify(header))}.${base64UrlEncode(JSON.stringify(claims))}`;\n this.cryptoKey ??= importPkcs8(this.key.private_key);\n const signature = await crypto.subtle.sign(\n 'RSASSA-PKCS1-v1_5',\n await this.cryptoKey,\n utf8(signingInput),\n );\n return `${signingInput}.${base64UrlEncode(new Uint8Array(signature))}`;\n }\n\n private async fetchToken(): Promise<string> {\n let assertion: string;\n try {\n assertion = await this.createAssertion();\n } catch {\n this.cryptoKey = undefined;\n throw new GscApiError({\n kind: 'auth',\n status: 0,\n googleMessage: '',\n advice:\n 'The service-account private key could not be read. Make sure GOOGLE_SERVICE_ACCOUNT_JSON contains the complete key file, including \"-----BEGIN PRIVATE KEY-----\".',\n });\n }\n\n const res = await this.fetchImpl(this.key.token_uri ?? GOOGLE_TOKEN_URL, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',\n assertion,\n }).toString(),\n });\n const body = (await res.json().catch(() => ({}))) as {\n access_token?: string;\n expires_in?: number;\n error?: string;\n error_description?: string;\n };\n if (!res.ok || !body.access_token) {\n const detail = [body.error, body.error_description].filter(Boolean).join(': ');\n throw new GscApiError({\n kind: 'auth',\n status: res.status,\n reason: body.error,\n googleMessage: detail,\n retryable: res.status >= 500,\n advice:\n body.error === 'invalid_grant'\n ? 'Google refused the service-account token (invalid_grant). Usually the key was deleted in Google Cloud, or this server’s clock is off by more than a few minutes. Create a new key and update GOOGLE_SERVICE_ACCOUNT_JSON.'\n : `Could not obtain a Google access token for the service account ${this.key.client_email}. Check that the key is valid and not disabled.`,\n });\n }\n this.cached = {\n token: body.access_token,\n expiresAt: this.now() + (body.expires_in ?? 3600) * 1000,\n };\n return body.access_token;\n }\n}\n\nasync function importPkcs8(pem: string): Promise<SigningKey> {\n const der = base64ToBytes(\n pem.replace(/-----(BEGIN|END) PRIVATE KEY-----/g, '').replace(/\\s+/g, ''),\n );\n return crypto.subtle.importKey(\n 'pkcs8',\n der,\n { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n}\n\n/** Per-instance cache of refreshed Google access tokens, keyed by user (OAuth mode). */\nexport type UserTokenCache = Map<string, { token: string; expiresAt: number }>;\n\n/**\n * A signed-in Google user's credentials (OAuth mode). Uses the access token carried in GSClaw's own\n * token while it is fresh, then refreshes with the user's refresh token, sharing results across\n * requests on this instance.\n */\nexport class UserTokenSource implements TokenSource {\n readonly identity: Identity;\n readonly scopes: string[];\n private readonly opts: {\n sub: string;\n accessToken?: string;\n /** Seconds since epoch. */\n expiresAt?: number;\n refreshToken: string;\n refresh: (refreshToken: string) => Promise<{ accessToken: string; expiresAt: number }>;\n cache: UserTokenCache;\n now?: () => number;\n };\n private inflight?: Promise<string>;\n\n constructor(email: string, scopes: string[], opts: UserTokenSource['opts']) {\n this.identity = { kind: 'user', email };\n this.scopes = scopes;\n this.opts = opts;\n }\n\n private nowMs(): number {\n return this.opts.now ? this.opts.now() : Date.now();\n }\n\n async getAccessToken(): Promise<string> {\n const now = this.nowMs();\n const { accessToken, expiresAt, cache, sub } = this.opts;\n if (accessToken && expiresAt && expiresAt * 1000 - EXPIRY_MARGIN_MS > now) return accessToken;\n const cached = cache.get(sub);\n if (cached && cached.expiresAt * 1000 - EXPIRY_MARGIN_MS > now) return cached.token;\n this.inflight ??= this.opts\n .refresh(this.opts.refreshToken)\n .then((fresh) => {\n cache.set(sub, { token: fresh.accessToken, expiresAt: fresh.expiresAt });\n return fresh.accessToken;\n })\n .finally(() => {\n this.inflight = undefined;\n });\n return this.inflight;\n }\n\n invalidate(): void {\n this.opts.accessToken = undefined;\n this.opts.cache.delete(this.opts.sub);\n }\n}\n","","import pkg from '../../package.json' with { type: 'json' };\n\nexport const NAME = 'gsclaw';\nexport const VERSION: string = pkg.version;\nexport const REPO_URL = 'https://github.com/tuyakhov/gsclaw';\n","import type { ConfigIssue } from './config.js';\nimport { REPO_URL, VERSION } from './version.js';\n\nexport const escape = (s: string) =>\n s.replace(\n /[&<>\"']/g,\n (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c]!,\n );\n\nexport const HTML_SECURITY_HEADERS: Record<string, string> = {\n 'content-type': 'text/html; charset=utf-8',\n 'content-security-policy':\n \"default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'self'; frame-ancestors 'none'\",\n 'x-content-type-options': 'nosniff',\n 'referrer-policy': 'no-referrer',\n 'x-frame-options': 'DENY',\n 'cache-control': 'no-store',\n};\n\nexport function layout(title: string, body: string): string {\n return `<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta name=\"robots\" content=\"noindex\"><title>${escape(title)}</title>\n<style>\n:root{color-scheme:light dark;--fg:#1c1b1a;--muted:#6b6862;--bg:#faf9f7;--card:#fff;--line:#e6e3de;--accent:#0d9488;--accent-text:#fff}\n@media (prefers-color-scheme:dark){:root{--fg:#ecebe8;--muted:#a19e98;--bg:#151413;--card:#1e1d1b;--line:#33312e;--accent:#2dd4bf;--accent-text:#062b27}}\nbody{margin:0;font:16px/1.55 system-ui,-apple-system,\"Segoe UI\",sans-serif;background:var(--bg);color:var(--fg);display:grid;place-items:center;min-height:100vh;padding:16px;box-sizing:border-box}\nmain{max-width:560px;width:100%;background:var(--card);border:1px solid var(--line);border-radius:14px;padding:28px}\nh1{font-size:1.25rem;margin:0 0 .5rem}p{margin:.5rem 0;color:var(--muted)}a{color:var(--accent)}\nul{padding-left:1.2rem}li{margin:.35rem 0}code{font-size:.9em}\nform{display:grid;gap:10px;margin-top:16px}label{font-weight:600;font-size:.9rem}\ninput{font:inherit;height:40px;padding:0 12px;border:1px solid var(--line);border-radius:8px;background:var(--bg);color:var(--fg)}\n.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:4px}\nbutton,.button{font:inherit;font-weight:600;height:40px;border-radius:8px;padding:0 16px;border:1px solid var(--line);background:var(--card);color:var(--fg);cursor:pointer;text-decoration:none;display:inline-flex;align-items:center}\n.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-text)}\n.client{background:var(--bg);border:1px solid var(--line);border-radius:10px;padding:12px 14px;margin:14px 0;color:var(--fg)}\n.client small{display:block;color:var(--muted)}.warn{color:#b45309}.error{color:#b91c1c;margin:0}\n</style></head><body><main>${body}</main></body></html>`;\n}\n\n/** Minimal public page: confirms the server runs, reveals nothing about its configuration. */\nexport function runningPage(): string {\n return layout(\n 'GSClaw',\n `<h1>GSClaw is running</h1>\n<p>This is a Google Search Console MCP server. Connect it from your AI client; the endpoint requires authentication.</p>\n<p><a href=\"${REPO_URL}#readme\">Documentation</a></p>`,\n );\n}\n\n/** Shown on serverless deployments that are missing configuration (names only, never values). */\nexport function setupPage(issues: ConfigIssue[]): string {\n const items = issues\n .map(\n (i) =>\n `<li>${i.variable ? `<code>${escape(i.variable)}</code> ` : ''}${escape(i.message)}</li>`,\n )\n .join('');\n return layout(\n 'GSClaw setup required',\n `<h1>GSClaw needs configuration</h1>\n<p>This deployment is not configured yet, so it refuses to serve requests. Set these environment variables in your hosting dashboard and redeploy:</p>\n<ul>${items}</ul>\n<p><a href=\"${REPO_URL}/blob/main/docs/setup.md\">Setup guide</a> · v${escape(VERSION)}</p>`,\n );\n}\n","/** GSClaw's own OAuth scopes (what a client may do), mapped to Google scopes internally. */\nexport const SCOPE_READ = 'gsc:read';\nexport const SCOPE_WRITE = 'gsc:write';\n\n/** Who is making a request, however they authenticated. */\nexport interface Principal {\n /** \"owner\" = the deployment owner (service-account mode); \"user\" = a Google user (OAuth mode). */\n kind: 'owner' | 'user';\n /** Stable subject: \"owner\", or the Google account's `sub`. */\n sub: string;\n email?: string;\n /** Granted GSClaw scopes. */\n scopes: string[];\n via: 'static_token' | 'secret_path' | 'oauth' | 'session' | 'stdio';\n /** OAuth client the token was issued to, if any. */\n clientId?: string;\n clientName?: string;\n /** The user's Google credentials (OAuth mode only). */\n google?: {\n accessToken?: string;\n /** Seconds since epoch. */\n expiresAt?: number;\n refreshToken: string;\n scope: string[];\n };\n}\n\nexport const OWNER: Principal = {\n kind: 'owner',\n sub: 'owner',\n scopes: [SCOPE_READ, SCOPE_WRITE],\n via: 'static_token',\n};\n","/**\n * Per-key token bucket (in memory, per instance). Refills `perMinute` tokens per minute with a\n * burst of the same size. A limit of 0 disables limiting.\n */\nexport class RateLimiter {\n private readonly buckets = new Map<string, { tokens: number; updated: number }>();\n\n constructor(\n private readonly perMinute: number,\n private readonly now: () => number = Date.now,\n ) {}\n\n check(key: string): { ok: true } | { ok: false; retryAfterSeconds: number } {\n if (this.perMinute <= 0) return { ok: true };\n const now = this.now();\n const ratePerMs = this.perMinute / 60_000;\n const bucket = this.buckets.get(key) ?? { tokens: this.perMinute, updated: now };\n bucket.tokens = Math.min(this.perMinute, bucket.tokens + (now - bucket.updated) * ratePerMs);\n bucket.updated = now;\n if (bucket.tokens >= 1) {\n bucket.tokens -= 1;\n this.buckets.set(key, bucket);\n this.prune(now);\n return { ok: true };\n }\n this.buckets.set(key, bucket);\n return { ok: false, retryAfterSeconds: Math.ceil((1 - bucket.tokens) / ratePerMs / 1000) };\n }\n\n private prune(now: number): void {\n if (this.buckets.size <= 1000) return;\n for (const [key, bucket] of this.buckets)\n if (now - bucket.updated > 120_000) this.buckets.delete(key);\n }\n}\n","// Dashboard session cookies. The value is a sealed blob (see seal.ts / oauth/server.ts); these\n// helpers only deal with the cookie itself.\n\nexport const SESSION_COOKIE = 'gsclaw_session';\nexport const SESSION_MAX_AGE_SECONDS = 7 * 24 * 3600;\n\nexport function parseCookies(header: string | null): Record<string, string> {\n const out: Record<string, string> = {};\n if (!header) return out;\n for (const part of header.split(';')) {\n const i = part.indexOf('=');\n if (i < 0) continue;\n const name = part.slice(0, i).trim();\n if (name) out[name] = part.slice(i + 1).trim();\n }\n return out;\n}\n\n/**\n * httpOnly + SameSite=Strict always. `Secure` everywhere except plain-http loopback development,\n * where some browsers would otherwise drop the cookie. Strict is enough even after the cross-site\n * return from Google sign-in: the HTML shell needs no cookie, and every API call is same-origin.\n */\nexport function sessionCookie(value: string, opts: { secure: boolean; maxAge?: number }): string {\n const attrs = [\n `${SESSION_COOKIE}=${value}`,\n 'Path=/',\n 'HttpOnly',\n 'SameSite=Strict',\n `Max-Age=${opts.maxAge ?? SESSION_MAX_AGE_SECONDS}`,\n ];\n if (opts.secure) attrs.push('Secure');\n return attrs.join('; ');\n}\n\nexport function clearedSessionCookie(secure: boolean): string {\n return sessionCookie('', { secure, maxAge: 0 });\n}\n\nexport const STATE_COOKIE = 'gsclaw_oauth_state';\n\n/**\n * Binds a Google sign-in to the browser that started it (login-CSRF protection). SameSite=Lax, not\n * Strict: it has to ride along on Google's cross-site redirect back to the callback, its only path.\n */\nexport function stateCookie(value: string, opts: { secure: boolean; maxAge: number }): string {\n const attrs = [\n `${STATE_COOKIE}=${value}`,\n 'Path=/oauth/google/callback',\n 'HttpOnly',\n 'SameSite=Lax',\n `Max-Age=${opts.maxAge}`,\n ];\n if (opts.secure) attrs.push('Secure');\n return attrs.join('; ');\n}\n","// OAuth clients without a database: Dynamic Client Registration hands out client IDs that are\n// sealed blobs of the client's metadata, and Client ID Metadata Documents (CIMD) are fetched from\n// the client's own URL. Redirect URIs are restricted to known MCP clients and loopback by default.\nimport type { Sealer } from '../seal.js';\n\nexport interface OAuthClient {\n clientId: string;\n name: string;\n redirectUris: string[];\n /** \"dcr\" = registered here; \"cimd\" = metadata document at the client_id URL. */\n type: 'dcr' | 'cimd';\n}\n\n/** Redirect hosts of popular remote-MCP clients. Extend with GSCLAW_OAUTH_REDIRECT_HOSTS. */\nexport const DEFAULT_REDIRECT_HOSTS = [\n 'claude.ai',\n 'claude.com',\n 'chatgpt.com',\n 'cursor.com',\n 'www.cursor.com',\n 'vscode.dev',\n 'insiders.vscode.dev',\n];\n\nconst LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]']);\nconst DCR_TTL_SECONDS = 10 * 365 * 24 * 3600;\n\nexport class OAuthError extends Error {\n constructor(\n readonly error: string,\n message: string,\n readonly status = 400,\n ) {\n super(message);\n }\n}\n\n/**\n * Loopback http redirects (desktop and CLI clients) are always allowed; https redirects must go to\n * a known client host or one listed in GSCLAW_OAUTH_REDIRECT_HOSTS (\"*\" allows any https host).\n */\nexport function redirectUriAllowed(uri: string, extraHosts: string[]): boolean {\n let url: URL;\n try {\n url = new URL(uri);\n } catch {\n return false;\n }\n if (url.hash || url.username || url.password) return false;\n if (url.protocol === 'http:') return LOOPBACK.has(url.hostname);\n if (url.protocol !== 'https:') return false;\n if (extraHosts.includes('*')) return true;\n return [...DEFAULT_REDIRECT_HOSTS, ...extraHosts].includes(url.hostname.toLowerCase());\n}\n\n/** Loopback redirect URIs match regardless of port (RFC 8252 §7.3). */\nexport function redirectUriMatches(registered: string, requested: string): boolean {\n if (registered === requested) return true;\n try {\n const a = new URL(registered);\n const b = new URL(requested);\n return (\n a.protocol === 'http:' &&\n LOOPBACK.has(a.hostname) &&\n a.hostname === b.hostname &&\n a.protocol === b.protocol &&\n a.pathname === b.pathname &&\n a.search === b.search\n );\n } catch {\n return false;\n }\n}\n\ninterface SealedClient {\n n: string;\n r: string[];\n}\n\n/** RFC 7591 registration. The returned client_id *is* the registration. */\nexport async function registerClient(\n body: unknown,\n sealer: Sealer,\n extraHosts: string[],\n): Promise<Record<string, unknown>> {\n const meta = (body && typeof body === 'object' ? body : {}) as Record<string, unknown>;\n const requested = meta.redirect_uris;\n if (!Array.isArray(requested) || requested.length === 0 || requested.length > 10) {\n throw new OAuthError('invalid_redirect_uri', 'redirect_uris must be a non-empty array.');\n }\n const redirectUris: string[] = [];\n for (const uri of requested as unknown[]) {\n if (typeof uri !== 'string' || !redirectUriAllowed(uri, extraHosts)) {\n throw new OAuthError(\n 'invalid_redirect_uri',\n `Redirect URI not allowed: ${String(uri).slice(0, 200)}. Use a loopback URI or a known client; the deployment owner can allow more hosts with GSCLAW_OAUTH_REDIRECT_HOSTS.`,\n );\n }\n redirectUris.push(uri);\n }\n const authMethod = meta.token_endpoint_auth_method ?? 'none';\n if (authMethod !== 'none') {\n throw new OAuthError(\n 'invalid_client_metadata',\n 'Only public clients are supported (token_endpoint_auth_method \"none\").',\n );\n }\n const name =\n typeof meta.client_name === 'string' && meta.client_name.trim()\n ? meta.client_name.trim().slice(0, 100)\n : 'MCP client';\n const clientId = await sealer.seal(\n 'client',\n { n: name, r: redirectUris } satisfies SealedClient,\n DCR_TTL_SECONDS,\n );\n return {\n client_id: clientId,\n client_id_issued_at: Math.floor(Date.now() / 1000),\n client_name: name,\n redirect_uris: redirectUris,\n grant_types: ['authorization_code', 'refresh_token'],\n response_types: ['code'],\n token_endpoint_auth_method: 'none',\n };\n}\n\nconst cimdCache = new Map<string, { client: OAuthClient; expiresAt: number }>();\n\n/** Refuses obviously internal targets. Workers cannot resolve DNS, so hostnames are allowed. */\nfunction publicHttpsUrl(raw: string): URL | null {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n return null;\n }\n if (url.protocol !== 'https:' || url.username || url.password || url.hash) return null;\n const host = url.hostname.toLowerCase();\n if (\n LOOPBACK.has(host) ||\n host.endsWith('.localhost') ||\n host.endsWith('.internal') ||\n host.endsWith('.local')\n )\n return null;\n if (/^\\[/.test(host)) return null; // IPv6 literals\n const ipv4 = /^(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)$/.exec(host);\n if (ipv4) {\n const [a, b] = [Number(ipv4[1]), Number(ipv4[2])];\n if (\n a === 10 ||\n a === 127 ||\n a === 0 ||\n (a === 169 && b === 254) ||\n (a === 172 && b >= 16 && b <= 31) ||\n (a === 192 && b === 168)\n ) {\n return null;\n }\n }\n return url;\n}\n\n/** Fetches and validates a Client ID Metadata Document, cached for 10 minutes per instance. */\nasync function resolveCimd(\n clientId: string,\n fetchImpl: typeof fetch,\n extraHosts: string[],\n): Promise<OAuthClient> {\n const cached = cimdCache.get(clientId);\n if (cached && cached.expiresAt > Date.now()) return cached.client;\n const url = publicHttpsUrl(clientId);\n if (!url) throw new OAuthError('invalid_client', 'client_id URL must be a public https URL.');\n let doc: Record<string, unknown>;\n try {\n const res = await fetchImpl(url.href, {\n headers: { accept: 'application/json' },\n redirect: 'error',\n signal: AbortSignal.timeout(5000),\n });\n const text = await res.text();\n if (!res.ok || text.length > 65_536) throw new Error(`HTTP ${res.status}`);\n doc = JSON.parse(text) as Record<string, unknown>;\n } catch {\n throw new OAuthError('invalid_client', 'Could not fetch the client metadata document.');\n }\n if (doc.client_id !== clientId)\n throw new OAuthError('invalid_client', 'Client metadata client_id does not match its URL.');\n const redirectUris = Array.isArray(doc.redirect_uris)\n ? doc.redirect_uris.filter((u): u is string => typeof u === 'string')\n : [];\n if (redirectUris.length === 0)\n throw new OAuthError('invalid_client', 'Client metadata has no redirect_uris.');\n if (typeof doc.client_name !== 'string' || !doc.client_name.trim()) {\n throw new OAuthError('invalid_client', 'Client metadata has no client_name.');\n }\n const client: OAuthClient = {\n clientId,\n name: doc.client_name.trim().slice(0, 100),\n redirectUris: redirectUris.filter((u) => redirectUriAllowed(u, extraHosts)),\n type: 'cimd',\n };\n cimdCache.set(clientId, { client, expiresAt: Date.now() + 10 * 60_000 });\n return client;\n}\n\n/** Looks up a client by ID: a sealed DCR registration or a CIMD URL. */\nexport async function resolveClient(\n clientId: string,\n deps: { sealer: Sealer; fetch: typeof fetch; extraHosts: string[] },\n): Promise<OAuthClient> {\n if (clientId.startsWith('https://')) return resolveCimd(clientId, deps.fetch, deps.extraHosts);\n const sealed = await deps.sealer.open<SealedClient>('client', clientId);\n if (!sealed)\n throw new OAuthError(\n 'invalid_client',\n 'Unknown client. Re-register the client (remove and re-add the connector).',\n 401,\n );\n return {\n clientId,\n name: sealed.n,\n redirectUris: sealed.r.filter((u) => redirectUriAllowed(u, deps.extraHosts)),\n type: 'dcr',\n };\n}\n\nexport function clearClientCache(): void {\n cimdCache.clear();\n}\n","// Google's OAuth 2.0 web-server flow (with PKCE), used by OAuth mode to learn who the user is and\n// obtain a refresh token for Search Console on their behalf.\nimport type { GoogleOAuthConfig } from '../config.js';\nimport { base64ToBytes } from '../crypto.js';\nimport { GscApiError } from '../errors.js';\n\nexport const GOOGLE_AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';\nexport const GOOGLE_TOKEN_URL = 'https://oauth2.googleapis.com/token';\n\nexport interface GoogleTokens {\n accessToken: string;\n /** Seconds since epoch. */\n expiresAt: number;\n refreshToken?: string;\n scope: string[];\n idToken?: string;\n}\n\nexport interface GoogleIdentity {\n sub: string;\n email: string;\n emailVerified: boolean;\n}\n\nexport function googleAuthUrl(\n cfg: GoogleOAuthConfig,\n opts: {\n redirectUri: string;\n scopes: string[];\n state: string;\n codeChallenge: string;\n loginHint?: string;\n },\n): string {\n const params = new URLSearchParams({\n client_id: cfg.clientId,\n redirect_uri: opts.redirectUri,\n response_type: 'code',\n scope: ['openid', 'email', ...opts.scopes].join(' '),\n access_type: 'offline',\n // Always ask, so Google returns a refresh token even for returning users.\n prompt: 'consent select_account',\n include_granted_scopes: 'false',\n state: opts.state,\n code_challenge: opts.codeChallenge,\n code_challenge_method: 'S256',\n });\n if (opts.loginHint) params.set('login_hint', opts.loginHint);\n return `${GOOGLE_AUTH_URL}?${params.toString()}`;\n}\n\ninterface TokenResponse {\n access_token?: string;\n expires_in?: number;\n refresh_token?: string;\n scope?: string;\n id_token?: string;\n error?: string;\n error_description?: string;\n}\n\nasync function tokenRequest(\n fetchImpl: typeof fetch,\n body: Record<string, string>,\n nowSeconds: number,\n): Promise<GoogleTokens> {\n const res = await fetchImpl(GOOGLE_TOKEN_URL, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams(body).toString(),\n });\n const data = (await res.json().catch(() => ({}))) as TokenResponse;\n if (!res.ok || !data.access_token) {\n throw new GscApiError({\n kind: 'auth',\n status: res.status,\n reason: data.error,\n googleMessage: [data.error, data.error_description].filter(Boolean).join(': '),\n advice:\n data.error === 'invalid_grant'\n ? 'Your Google authorization has expired or was revoked. Sign in to GSClaw again.'\n : 'Google rejected the sign-in. Check GOOGLE_OAUTH_CLIENT_ID / GOOGLE_OAUTH_CLIENT_SECRET and the authorized redirect URI.',\n });\n }\n return {\n accessToken: data.access_token,\n expiresAt: nowSeconds + (data.expires_in ?? 3600),\n refreshToken: data.refresh_token,\n scope: (data.scope ?? '').split(' ').filter(Boolean),\n idToken: data.id_token,\n };\n}\n\nexport function exchangeGoogleCode(\n cfg: GoogleOAuthConfig,\n opts: {\n code: string;\n redirectUri: string;\n codeVerifier: string;\n fetch: typeof fetch;\n nowSeconds: number;\n },\n): Promise<GoogleTokens> {\n return tokenRequest(\n opts.fetch,\n {\n grant_type: 'authorization_code',\n code: opts.code,\n redirect_uri: opts.redirectUri,\n client_id: cfg.clientId,\n client_secret: cfg.clientSecret,\n code_verifier: opts.codeVerifier,\n },\n opts.nowSeconds,\n );\n}\n\nexport function refreshGoogleToken(\n cfg: GoogleOAuthConfig,\n opts: { refreshToken: string; fetch: typeof fetch; nowSeconds: number },\n): Promise<GoogleTokens> {\n return tokenRequest(\n opts.fetch,\n {\n grant_type: 'refresh_token',\n refresh_token: opts.refreshToken,\n client_id: cfg.clientId,\n client_secret: cfg.clientSecret,\n },\n opts.nowSeconds,\n );\n}\n\n/**\n * Reads the identity from Google's ID token. The token was received directly from Google's token\n * endpoint over TLS (not via the browser), so OpenID Connect allows skipping signature validation;\n * we still check issuer, audience and expiry.\n */\nexport function identityFromIdToken(\n idToken: string | undefined,\n cfg: GoogleOAuthConfig,\n nowSeconds: number,\n): GoogleIdentity | null {\n if (!idToken) return null;\n try {\n const payload = JSON.parse(\n new TextDecoder().decode(base64ToBytes(idToken.split('.')[1] ?? '')),\n ) as {\n iss?: string;\n aud?: string;\n exp?: number;\n sub?: string;\n email?: string;\n email_verified?: boolean;\n };\n if (payload.iss !== 'https://accounts.google.com' && payload.iss !== 'accounts.google.com')\n return null;\n if (payload.aud !== cfg.clientId) return null;\n if (!payload.exp || payload.exp < nowSeconds - 300) return null;\n if (!payload.sub || !payload.email) return null;\n return {\n sub: payload.sub,\n email: payload.email.toLowerCase(),\n emailVerified: payload.email_verified === true,\n };\n } catch {\n return null;\n }\n}\n\n/** True when the allowlists permit this identity (no lists configured = everyone). */\nexport function isAllowed(identity: GoogleIdentity, cfg: GoogleOAuthConfig): boolean {\n if (cfg.allowedEmails.length === 0 && cfg.allowedDomains.length === 0) return true;\n if (!identity.emailVerified) return false;\n if (cfg.allowedEmails.includes(identity.email)) return true;\n const domain = identity.email.split('@')[1] ?? '';\n return cfg.allowedDomains.includes(domain);\n}\n","import { HTML_SECURITY_HEADERS, escape, layout } from '../pages.js';\n\nexport interface ConsentInfo {\n clientName: string;\n redirectUri: string;\n /** Sealed authorization request, posted back with the decision. */\n request: string;\n canWrite: boolean;\n error?: string;\n}\n\nfunction hostOf(uri: string): string {\n try {\n const url = new URL(uri);\n return url.protocol === 'http:' ? `${url.hostname} (an app on this computer)` : url.host;\n } catch {\n return uri;\n }\n}\n\n/**\n * HTML headers for consent pages. Browsers apply `form-action` to the redirect that follows a form\n * submission, so the client's redirect origin must be allowed alongside 'self'.\n */\nexport function consentHeaders(\n redirectUri: string,\n extraOrigins: string[] = [],\n): Record<string, string> {\n const origins = [...extraOrigins];\n try {\n origins.push(new URL(redirectUri).origin);\n } catch {\n // keep 'self' only\n }\n return {\n ...HTML_SECURITY_HEADERS,\n 'content-security-policy': HTML_SECURITY_HEADERS['content-security-policy']!.replace(\n \"form-action 'self'\",\n [\"form-action 'self'\", ...origins].join(' '),\n ),\n };\n}\n\nfunction clientBlock(info: ConsentInfo): string {\n return `<div class=\"client\"><strong>${escape(info.clientName)}</strong>\n<small>will receive access at ${escape(hostOf(info.redirectUri))}</small></div>`;\n}\n\nfunction access(info: ConsentInfo): string {\n return info.canWrite\n ? 'read your Search Console data and submit or remove sitemaps'\n : 'read your Search Console data (read-only)';\n}\n\n/** Service-account mode: the owner confirms with the deployment's access token. */\nexport function ownerConsentPage(info: ConsentInfo): string {\n return layout(\n 'Connect to GSClaw',\n `<h1>Allow access to GSClaw?</h1>\n${clientBlock(info)}\n<p>This app wants to ${access(info)} through this GSClaw deployment. Only continue if you started this connection.</p>\n<form method=\"post\" action=\"/oauth/authorize\">\n<input type=\"hidden\" name=\"request\" value=\"${escape(info.request)}\">\n<label for=\"token\">Access token</label>\n<input id=\"token\" name=\"token\" type=\"password\" autocomplete=\"current-password\" required autofocus>\n${info.error ? `<p class=\"error\" role=\"alert\">${escape(info.error)}</p>` : ''}\n<div class=\"actions\">\n<button class=\"primary\" type=\"submit\" name=\"decision\" value=\"allow\">Allow</button>\n<button type=\"submit\" name=\"decision\" value=\"deny\" formnovalidate>Deny</button>\n</div>\n</form>`,\n );\n}\n\n/** OAuth mode: confirm the client, then continue to Google sign-in. */\nexport function googleConsentPage(info: ConsentInfo): string {\n return layout(\n 'Connect to GSClaw',\n `<h1>Allow access to GSClaw?</h1>\n${clientBlock(info)}\n<p>This app wants to ${access(info)} through this GSClaw deployment. Sign in with the Google account that has access to your Search Console properties.</p>\n<form method=\"post\" action=\"/oauth/authorize\">\n<input type=\"hidden\" name=\"request\" value=\"${escape(info.request)}\">\n<div class=\"actions\">\n<button class=\"primary\" type=\"submit\" name=\"decision\" value=\"allow\">Continue with Google</button>\n<button type=\"submit\" name=\"decision\" value=\"deny\">Deny</button>\n</div>\n</form>`,\n );\n}\n\nexport function oauthErrorPage(title: string, message: string): string {\n return layout('GSClaw sign-in', `<h1>${escape(title)}</h1><p>${escape(message)}</p>`);\n}\n","// GSClaw's built-in OAuth 2.1 authorization server for MCP clients (claude.ai, ChatGPT, Claude Code,\n// Cursor, VS Code…). Stateless: every artifact (client registration, authorization code, access and\n// refresh token, Google round-trip state) is a sealed, expiring blob.\n//\n// Service-account mode: the owner approves a client by entering GSCLAW_ACCESS_TOKEN on the consent page.\n// OAuth mode: the consent page hands off to Google; tokens wrap the user's Google credentials.\nimport type { Config } from '../config.js';\nimport { base64UrlEncode, randomToken, sha256, timingSafeEqual } from '../crypto.js';\nimport { GscApiError } from '../errors.js';\nimport { SCOPE_READONLY, SCOPE_READWRITE } from '../gsc/auth.js';\nimport type { Logger } from '../log.js';\nimport { HTML_SECURITY_HEADERS } from '../pages.js';\nimport { SCOPE_READ, SCOPE_WRITE, type Principal } from '../principal.js';\nimport { RateLimiter } from '../ratelimit.js';\nimport type { Sealer } from '../seal.js';\nimport { parseCookies, sessionCookie, STATE_COOKIE, stateCookie } from '../session.js';\nimport { OAuthError, redirectUriMatches, registerClient, resolveClient } from './clients.js';\nimport {\n exchangeGoogleCode,\n googleAuthUrl,\n identityFromIdToken,\n isAllowed,\n refreshGoogleToken,\n} from './google.js';\nimport { consentHeaders, googleConsentPage, oauthErrorPage, ownerConsentPage } from './pages.js';\n\nconst REQUEST_TTL = 15 * 60;\nconst CODE_TTL = 60;\nconst ACCESS_TTL = 3600;\nconst REFRESH_TTL = 90 * 24 * 3600;\nexport const SESSION_TTL = 7 * 24 * 3600;\n\n/** Compact principal stored inside sealed tokens. */\ninterface SealedPrincipal {\n k: 'owner' | 'user';\n sub: string;\n email?: string;\n /** Google verified the email (allowlists only ever match verified emails). */\n ev?: boolean;\n g?: { at?: string; ex?: number; rt: string; sc: string[] };\n}\n\n/** A validated authorization request, sealed into the consent form and the Google state. */\ninterface AuthRequest {\n cid: string;\n cn: string;\n ru: string;\n cc: string;\n st?: string;\n sc: string[];\n res: string;\n}\n\ninterface GoogleState {\n req?: AuthRequest;\n dash?: boolean;\n /** PKCE verifier for the Google round-trip. */\n v: string;\n /** Browser binding: must match the state cookie set when the round-trip started. */\n b: string;\n}\n\ninterface CodePayload extends Omit<AuthRequest, 'st'> {\n p: SealedPrincipal;\n}\n\ninterface TokenPayload {\n cid: string;\n cn: string;\n sc: string[];\n aud: string;\n p: SealedPrincipal;\n}\n\nexport interface SessionPayload {\n p: SealedPrincipal;\n}\n\nexport interface OAuthServerDeps {\n config: Config;\n sealer: Sealer;\n logger: Logger;\n fetch: typeof fetch;\n now: () => Date;\n}\n\nconst CORS = {\n 'access-control-allow-origin': '*',\n 'access-control-allow-methods': 'GET, POST, OPTIONS',\n 'access-control-allow-headers': 'authorization, content-type, mcp-protocol-version',\n};\n\nfunction json(body: unknown, status = 200, headers: Record<string, string> = {}): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: {\n 'content-type': 'application/json',\n 'cache-control': 'no-store',\n pragma: 'no-cache',\n ...CORS,\n ...headers,\n },\n });\n}\n\nfunction html(\n body: string,\n status = 200,\n headers: Record<string, string> = HTML_SECURITY_HEADERS,\n): Response {\n return new Response(body, { status, headers });\n}\n\nfunction redirect(location: string, headers: Record<string, string> = {}): Response {\n return new Response(null, {\n status: 302,\n headers: { location, 'cache-control': 'no-store', ...headers },\n });\n}\n\nfunction withParams(uri: string, params: Record<string, string | undefined>): string {\n const url = new URL(uri);\n for (const [k, v] of Object.entries(params)) if (v !== undefined) url.searchParams.set(k, v);\n return url.href;\n}\n\nasync function readForm(request: Request): Promise<Record<string, string>> {\n const type = request.headers.get('content-type') ?? '';\n const text = await request.text();\n if (type.includes('application/json')) {\n try {\n const body = JSON.parse(text) as Record<string, unknown>;\n return Object.fromEntries(\n Object.entries(body).map(([k, v]) => [k, typeof v === 'string' ? v : String(v)]),\n );\n } catch {\n return {};\n }\n }\n return Object.fromEntries(new URLSearchParams(text));\n}\n\nexport function toPrincipal(\n p: SealedPrincipal,\n extra: Pick<Principal, 'scopes' | 'via' | 'clientId' | 'clientName'>,\n): Principal {\n return {\n kind: p.k,\n sub: p.sub,\n email: p.email,\n ...extra,\n google: p.g\n ? { accessToken: p.g.at, expiresAt: p.g.ex, refreshToken: p.g.rt, scope: p.g.sc }\n : undefined,\n };\n}\n\nexport function createOAuthServer(deps: OAuthServerDeps) {\n const { config, sealer, logger } = deps;\n const nowSeconds = () => Math.floor(deps.now().getTime() / 1000);\n const consentLimiter = new RateLimiter(10);\n const scopesSupported = config.allowWrites ? [SCOPE_READ, SCOPE_WRITE] : [SCOPE_READ];\n const google = config.googleOAuth;\n const clientDeps = { sealer, fetch: deps.fetch, extraHosts: config.oauthRedirectHosts };\n\n const resourceUrl = (base: string) => `${base}/mcp`;\n const metadataUrl = (base: string) => `${base}/.well-known/oauth-protected-resource/mcp`;\n\n function grantedScopes(requested: string | undefined | null): string[] {\n const wanted = (requested ?? '').split(/\\s+/).filter((s) => scopesSupported.includes(s));\n const scopes = new Set(wanted.length > 0 ? wanted : [SCOPE_READ]);\n scopes.add(SCOPE_READ);\n return [...scopes];\n }\n\n const googleScopeFor = (scopes: string[]) =>\n scopes.includes(SCOPE_WRITE) ? SCOPE_READWRITE : SCOPE_READONLY;\n\n function protectedResourceMetadata(base: string) {\n return {\n resource: resourceUrl(base),\n authorization_servers: [base],\n scopes_supported: scopesSupported,\n bearer_methods_supported: ['header'],\n resource_name: 'GSClaw',\n resource_documentation: 'https://github.com/tuyakhov/gsclaw#readme',\n };\n }\n\n function authorizationServerMetadata(base: string) {\n return {\n issuer: base,\n authorization_endpoint: `${base}/oauth/authorize`,\n token_endpoint: `${base}/oauth/token`,\n registration_endpoint: `${base}/oauth/register`,\n scopes_supported: scopesSupported,\n response_types_supported: ['code'],\n response_modes_supported: ['query'],\n grant_types_supported: ['authorization_code', 'refresh_token'],\n token_endpoint_auth_methods_supported: ['none'],\n code_challenge_methods_supported: ['S256'],\n client_id_metadata_document_supported: true,\n authorization_response_iss_parameter_supported: true,\n service_documentation: 'https://github.com/tuyakhov/gsclaw#readme',\n };\n }\n\n /**\n * The allowlist applies to every request, not just sign-in, so removing someone from\n * ALLOWED_GOOGLE_EMAILS/DOMAINS (and redeploying) cuts off their existing tokens and sessions too.\n */\n const stillAllowed = (p: SealedPrincipal): boolean =>\n p.k !== 'user' ||\n !google ||\n isAllowed({ sub: p.sub, email: p.email ?? '', emailVerified: p.ev === true }, google);\n\n async function issueCode(req: AuthRequest, p: SealedPrincipal, base: string): Promise<Response> {\n const code = await sealer.seal(\n 'code',\n {\n cid: req.cid,\n cn: req.cn,\n ru: req.ru,\n cc: req.cc,\n sc: req.sc,\n res: req.res,\n p,\n } satisfies CodePayload,\n CODE_TTL,\n );\n logger.info('oauth code issued', { client: req.cn, kind: p.k });\n return redirect(withParams(req.ru, { code, state: req.st, iss: base }));\n }\n\n const clientError = (req: AuthRequest, error: string, description: string, base: string) =>\n redirect(\n withParams(req.ru, { error, error_description: description, state: req.st, iss: base }),\n );\n\n async function startGoogle(\n target: Pick<GoogleState, 'req' | 'dash'>,\n scopes: string[],\n base: string,\n ): Promise<Response> {\n const state: GoogleState = { ...target, v: randomToken(32), b: randomToken(24) };\n const challenge = base64UrlEncode(await sha256(state.v));\n const sealedState = await sealer.seal('state', state, REQUEST_TTL);\n const secure = new URL(base).protocol === 'https:';\n return redirect(\n googleAuthUrl(google!, {\n redirectUri: `${base}/oauth/google/callback`,\n scopes: [googleScopeFor(scopes)],\n state: sealedState,\n codeChallenge: challenge,\n }),\n { 'set-cookie': stateCookie(state.b, { secure, maxAge: REQUEST_TTL }) },\n );\n }\n\n /** GET /oauth/authorize: validate, then show the consent page. */\n async function authorize(url: URL, base: string): Promise<Response> {\n const q = url.searchParams;\n const clientId = q.get('client_id');\n if (!clientId) return html(oauthErrorPage('Invalid request', 'client_id is missing.'), 400);\n let client;\n try {\n client = await resolveClient(clientId, clientDeps);\n } catch (error) {\n return html(\n oauthErrorPage('Unknown app', error instanceof Error ? error.message : 'Unknown client.'),\n 400,\n );\n }\n const requested =\n q.get('redirect_uri') ?? (client.redirectUris.length === 1 ? client.redirectUris[0]! : '');\n if (!client.redirectUris.some((r) => redirectUriMatches(r, requested))) {\n return html(\n oauthErrorPage('Invalid redirect', 'The redirect_uri is not registered for this app.'),\n 400,\n );\n }\n const req: AuthRequest = {\n cid: clientId,\n cn: client.name,\n ru: requested,\n cc: q.get('code_challenge') ?? '',\n st: q.get('state') ?? undefined,\n sc: grantedScopes(q.get('scope')),\n res: resourceUrl(base),\n };\n if (q.get('response_type') !== 'code')\n return clientError(\n req,\n 'unsupported_response_type',\n 'Only response_type=code is supported.',\n base,\n );\n if (!req.cc || q.get('code_challenge_method') !== 'S256') {\n return clientError(\n req,\n 'invalid_request',\n 'PKCE with code_challenge_method=S256 is required.',\n base,\n );\n }\n const resource = q.get('resource');\n if (resource && resource.replace(/\\/$/, '') !== req.res) {\n return clientError(req, 'invalid_target', `This server's resource is ${req.res}.`, base);\n }\n const sealedReq = await sealer.seal('request', req, REQUEST_TTL);\n const info = {\n clientName: client.name,\n redirectUri: req.ru,\n request: sealedReq,\n canWrite: req.sc.includes(SCOPE_WRITE),\n };\n return config.authMode === 'oauth'\n ? html(googleConsentPage(info), 200, consentHeaders(req.ru, ['https://accounts.google.com']))\n : html(ownerConsentPage(info), 200, consentHeaders(req.ru));\n }\n\n /** POST /oauth/authorize: the consent decision. */\n async function decide(request: Request, base: string): Promise<Response> {\n const form = await readForm(request);\n const req = await sealer.open<AuthRequest>('request', form.request);\n if (!req)\n return html(\n oauthErrorPage(\n 'Sign-in expired',\n 'This sign-in page expired. Start the connection again from your app.',\n ),\n 400,\n );\n if (form.decision !== 'allow')\n return clientError(req, 'access_denied', 'The request was denied.', base);\n\n if (config.authMode === 'oauth') return startGoogle({ req }, req.sc, base);\n\n const ip =\n request.headers.get('cf-connecting-ip') ??\n request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ??\n 'local';\n const info = {\n clientName: req.cn,\n redirectUri: req.ru,\n request: form.request!,\n canWrite: req.sc.includes(SCOPE_WRITE),\n };\n if (!consentLimiter.check(ip).ok) {\n return html(\n ownerConsentPage({ ...info, error: 'Too many attempts. Wait a minute and try again.' }),\n 429,\n consentHeaders(req.ru),\n );\n }\n if (!form.token || !(await timingSafeEqual(form.token.trim(), config.accessToken!))) {\n logger.warn('oauth owner consent: wrong access token');\n return html(\n ownerConsentPage({ ...info, error: 'That access token is not correct.' }),\n 401,\n consentHeaders(req.ru),\n );\n }\n return issueCode(req, { k: 'owner', sub: 'owner' }, base);\n }\n\n /** GET /oauth/google/callback (OAuth mode): finish Google sign-in for an MCP client or the dashboard. */\n async function googleCallback(request: Request, url: URL, base: string): Promise<Response> {\n const state = await sealer.open<GoogleState>('state', url.searchParams.get('state'));\n const bound = parseCookies(request.headers.get('cookie'))[STATE_COOKIE];\n if (!state || !google || !bound || !(await timingSafeEqual(bound, state.b)))\n return html(\n oauthErrorPage(\n 'Sign-in expired',\n 'This sign-in expired or was started in a different browser. Please start again.',\n ),\n 400,\n );\n const fail = (error: string, description: string, dashCode: string) =>\n state.req\n ? clientError(state.req, error, description, base)\n : redirect(`/?login_error=${dashCode}`);\n\n if (url.searchParams.get('error') || !url.searchParams.get('code'))\n return fail('access_denied', 'Google sign-in was cancelled.', 'cancelled');\n\n let tokens;\n try {\n tokens = await exchangeGoogleCode(google, {\n code: url.searchParams.get('code')!,\n redirectUri: `${base}/oauth/google/callback`,\n codeVerifier: state.v,\n fetch: deps.fetch,\n nowSeconds: nowSeconds(),\n });\n } catch (error) {\n logger.warn('google code exchange failed', { error });\n return fail('server_error', 'Google sign-in failed.', 'google_error');\n }\n const identity = identityFromIdToken(tokens.idToken, google, nowSeconds());\n if (!identity)\n return fail('server_error', 'Google did not return a verified identity.', 'google_error');\n if (!isAllowed(identity, google)) {\n logger.warn('google account not allowed', { domain: identity.email.split('@')[1] });\n return fail(\n 'access_denied',\n `${identity.email} is not allowed to use this GSClaw deployment.`,\n 'not_allowed',\n );\n }\n const needed = state.req ? googleScopeFor(state.req.sc) : SCOPE_READONLY;\n const granted =\n tokens.scope.includes(needed) ||\n (needed === SCOPE_READONLY && tokens.scope.includes(SCOPE_READWRITE));\n if (!granted)\n return fail(\n 'access_denied',\n 'Search Console access was not granted. Try again and keep the Search Console permission ticked.',\n 'scope',\n );\n if (!tokens.refreshToken) {\n return fail(\n 'server_error',\n 'Google did not return a refresh token. Remove GSClaw from your Google account’s third-party access and try again.',\n 'google_error',\n );\n }\n\n const p: SealedPrincipal = {\n k: 'user',\n sub: identity.sub,\n email: identity.email,\n ev: identity.emailVerified,\n g: {\n at: tokens.accessToken,\n ex: tokens.expiresAt,\n rt: tokens.refreshToken,\n sc: tokens.scope,\n },\n };\n if (state.dash) {\n const value = await sealer.seal('session', { p } satisfies SessionPayload, SESSION_TTL);\n const secure = new URL(base).protocol === 'https:';\n return redirect('/', { 'set-cookie': sessionCookie(value, { secure, maxAge: SESSION_TTL }) });\n }\n return issueCode(state.req!, p, base);\n }\n\n async function issueTokens(t: Omit<TokenPayload, 'p'>, p: SealedPrincipal): Promise<Response> {\n let ttl = ACCESS_TTL;\n if (p.g?.ex) ttl = Math.max(300, Math.min(ACCESS_TTL, p.g.ex - nowSeconds() - 120));\n const access = await sealer.seal('access', { ...t, p } satisfies TokenPayload, ttl);\n // The refresh token keeps only the Google refresh token; access tokens are re-derived on refresh.\n const refreshPrincipal: SealedPrincipal = p.g ? { ...p, g: { rt: p.g.rt, sc: p.g.sc } } : p;\n const refresh = await sealer.seal(\n 'refresh',\n { ...t, p: refreshPrincipal } satisfies TokenPayload,\n REFRESH_TTL,\n );\n return json({\n access_token: access,\n token_type: 'Bearer',\n expires_in: ttl,\n refresh_token: refresh,\n scope: t.sc.join(' '),\n });\n }\n\n /** POST /oauth/token */\n async function token(request: Request): Promise<Response> {\n const form = await readForm(request);\n const basic = request.headers.get('authorization');\n let clientId = form.client_id;\n if (!clientId && basic?.startsWith('Basic ')) {\n try {\n clientId = decodeURIComponent(atob(basic.slice(6)).split(':')[0] ?? '');\n } catch {\n clientId = undefined;\n }\n }\n const err = (error: string, description: string, status = 400) =>\n json({ error, error_description: description }, status);\n\n if (form.grant_type === 'authorization_code') {\n const code = await sealer.open<CodePayload>('code', form.code);\n if (!code) return err('invalid_grant', 'The authorization code is invalid or expired.');\n if (code.cid !== clientId)\n return err('invalid_grant', 'The code was issued to another client.');\n if (!form.redirect_uri || !redirectUriMatches(code.ru, form.redirect_uri))\n return err('invalid_grant', 'redirect_uri does not match.');\n if (!form.code_verifier || base64UrlEncode(await sha256(form.code_verifier)) !== code.cc) {\n return err('invalid_grant', 'PKCE verification failed.');\n }\n if (form.resource && form.resource.replace(/\\/$/, '') !== code.res)\n return err('invalid_target', 'Unknown resource.');\n return issueTokens({ cid: code.cid, cn: code.cn, sc: code.sc, aud: code.res }, code.p);\n }\n\n if (form.grant_type === 'refresh_token') {\n const rt = await sealer.open<TokenPayload>('refresh', form.refresh_token);\n if (!rt) return err('invalid_grant', 'The refresh token is invalid or expired.');\n if (clientId && clientId !== rt.cid)\n return err('invalid_grant', 'The refresh token was issued to another client.');\n if (!stillAllowed(rt.p))\n return err('invalid_grant', 'This Google account is no longer allowed to use GSClaw.');\n let scopes = rt.sc;\n if (form.scope) {\n const narrowed = form.scope.split(/\\s+/).filter((s) => rt.sc.includes(s));\n if (narrowed.length === 0)\n return err('invalid_scope', 'Requested scope exceeds the original grant.');\n scopes = narrowed;\n }\n let p = rt.p;\n if (p.g && google) {\n try {\n const fresh = await refreshGoogleToken(google, {\n refreshToken: p.g.rt,\n fetch: deps.fetch,\n nowSeconds: nowSeconds(),\n });\n p = {\n ...p,\n g: {\n at: fresh.accessToken,\n ex: fresh.expiresAt,\n rt: fresh.refreshToken ?? p.g.rt,\n sc: p.g.sc,\n },\n };\n } catch (error) {\n if (error instanceof GscApiError && error.reason === 'invalid_grant') {\n return err('invalid_grant', 'Google access was revoked or expired. Sign in again.');\n }\n return err(\n 'temporarily_unavailable',\n 'Could not refresh Google credentials. Try again.',\n 503,\n );\n }\n }\n return issueTokens({ cid: rt.cid, cn: rt.cn, sc: scopes, aud: rt.aud }, p);\n }\n\n return err('unsupported_grant_type', 'Use authorization_code or refresh_token.');\n }\n\n return {\n resourceUrl,\n\n /** WWW-Authenticate value for /mcp 401s (RFC 9728 discovery). */\n challenge(base: string, error?: 'invalid_token'): string {\n const parts = [\n `resource_metadata=\"${metadataUrl(base)}\"`,\n `scope=\"${scopesSupported.join(' ')}\"`,\n ];\n if (error) parts.push(`error=\"${error}\"`);\n return `Bearer ${parts.join(', ')}`;\n },\n\n /** Validates a GSClaw-issued access token for this resource. */\n async verifyAccessToken(token: string, base: string): Promise<Principal | null> {\n const t = await sealer.open<TokenPayload>('access', token);\n if (!t || t.aud !== resourceUrl(base)) return null;\n if ((config.authMode === 'oauth') !== (t.p.k === 'user') || !stillAllowed(t.p)) return null;\n return toPrincipal(t.p, { scopes: t.sc, via: 'oauth', clientId: t.cid, clientName: t.cn });\n },\n\n /** Reads a dashboard session cookie value. */\n async openSession(value: string | undefined): Promise<Principal | null> {\n const s = await sealer.open<SessionPayload>('session', value);\n if (!s) return null;\n if ((config.authMode === 'oauth') !== (s.p.k === 'user') || !stillAllowed(s.p)) return null;\n return toPrincipal(s.p, { scopes: [SCOPE_READ], via: 'session' });\n },\n\n async ownerSession(): Promise<string> {\n return sealer.seal(\n 'session',\n { p: { k: 'owner', sub: 'owner' } } satisfies SessionPayload,\n SESSION_TTL,\n );\n },\n\n /** Routes /.well-known/* and /oauth/*; returns null for anything else. */\n async handle(request: Request, url: URL, path: string, base: string): Promise<Response | null> {\n const method = request.method;\n const isWellKnown = path.startsWith('/.well-known/');\n if (!isWellKnown && !path.startsWith('/oauth/')) return null;\n if (method === 'OPTIONS') return new Response(null, { status: 204, headers: CORS });\n\n try {\n if (\n path === '/.well-known/oauth-protected-resource' ||\n path === '/.well-known/oauth-protected-resource/mcp'\n ) {\n return json(protectedResourceMetadata(base));\n }\n if (\n path === '/.well-known/oauth-authorization-server' ||\n path === '/.well-known/oauth-authorization-server/mcp'\n ) {\n return json(authorizationServerMetadata(base));\n }\n if (path === '/oauth/register' && method === 'POST') {\n const body: unknown = await request.json().catch(() => null);\n return json(await registerClient(body, sealer, config.oauthRedirectHosts), 201);\n }\n if (path === '/oauth/authorize' && method === 'GET') return authorize(url, base);\n if (path === '/oauth/authorize' && method === 'POST') return decide(request, base);\n if (path === '/oauth/token' && method === 'POST') return token(request);\n if (path === '/oauth/google/callback' && method === 'GET')\n return googleCallback(request, url, base);\n if (path === '/oauth/google/start' && method === 'GET' && google) {\n return startGoogle({ dash: true }, [SCOPE_READ], base);\n }\n } catch (error) {\n if (error instanceof OAuthError) {\n return json({ error: error.error, error_description: error.message }, error.status);\n }\n throw error;\n }\n return json({ error: 'not_found' }, 404);\n },\n };\n}\n\nexport type OAuthServer = ReturnType<typeof createOAuthServer>;\n","import { VERSION } from './version.js';\n\nconst RELEASES_URL = 'https://api.github.com/repos/tuyakhov/gsclaw/releases/latest';\nconst CACHE_MS = 6 * 3600_000;\n\nlet cached: { at: number; latest: string | null } | undefined;\n\n/** Compares dotted numeric versions; pre-release suffixes are ignored. */\nexport function isNewer(candidate: string, current: string): boolean {\n const parse = (v: string) =>\n v\n .replace(/^v/, '')\n .split('-')[0]!\n .split('.')\n .map((n) => Number(n) || 0);\n const a = parse(candidate);\n const b = parse(current);\n for (let i = 0; i < Math.max(a.length, b.length); i++) {\n const diff = (a[i] ?? 0) - (b[i] ?? 0);\n if (diff !== 0) return diff > 0;\n }\n return false;\n}\n\n/**\n * Latest published GSClaw release (cached for 6 hours per instance). Any failure (offline,\n * rate-limited, no releases yet) yields null so the dashboard simply shows no notice.\n */\nexport async function latestRelease(\n fetchImpl: typeof fetch = (input, init) => fetch(input, init),\n now = Date.now(),\n): Promise<string | null> {\n if (cached && now - cached.at < CACHE_MS) return cached.latest;\n let latest: string | null = null;\n try {\n const res = await fetchImpl(RELEASES_URL, {\n headers: { accept: 'application/vnd.github+json', 'user-agent': `gsclaw/${VERSION}` },\n signal: AbortSignal.timeout(3000),\n });\n if (res.ok) {\n const body = (await res.json()) as { tag_name?: unknown };\n if (typeof body.tag_name === 'string') latest = body.tag_name.replace(/^v/, '');\n }\n } catch {\n latest = null;\n }\n cached = { at: now, latest };\n return latest;\n}\n\nexport function resetUpdateCheckCache(): void {\n cached = undefined;\n}\n","import type { Config } from './config.js';\nimport { timingSafeEqual } from './crypto.js';\nimport { publicErrorMessage } from './errors.js';\nimport { listProperties } from './gsc/sites.js';\nimport type { Logger } from './log.js';\nimport type { OAuthServer } from './oauth/server.js';\nimport { SESSION_TTL } from './oauth/server.js';\nimport type { Principal } from './principal.js';\nimport { RateLimiter } from './ratelimit.js';\nimport type { Runtime } from './runtime.js';\nimport { clearedSessionCookie, parseCookies, SESSION_COOKIE, sessionCookie } from './session.js';\nimport { isNewer, latestRelease } from './update-check.js';\nimport { VERSION } from './version.js';\n\nexport interface DashboardApiDeps {\n config: Config;\n runtime: Runtime;\n oauth: OAuthServer;\n logger: Logger;\n platform: string;\n fetch?: typeof fetch;\n now?: () => Date;\n baseUrl: (request: Request, url: URL) => string;\n}\n\nfunction json(body: unknown, status = 200, headers: Record<string, string> = {}): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json', 'cache-control': 'no-store', ...headers },\n });\n}\n\nconst LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]']);\n\n/**\n * JSON API behind the dashboard. Every data endpoint runs the same tool implementations the MCP\n * server uses (`POST /api/tools/:name`) with the signed-in caller's own credentials, so the dashboard\n * and the AI always see identical numbers.\n */\nexport function createDashboardApi(deps: DashboardApiDeps) {\n const { config, runtime, logger, oauth } = deps;\n const isOAuth = config.authMode === 'oauth';\n const publicOrigin = config.publicBaseUrl ? new URL(config.publicBaseUrl).origin : null;\n const loginLimiter = new RateLimiter(10);\n\n const isSecure = (url: URL) => !(url.protocol === 'http:' && LOOPBACK.has(url.hostname));\n\n async function signedIn(request: Request): Promise<Principal | null> {\n return oauth.openSession(parseCookies(request.headers.get('cookie'))[SESSION_COOKIE]);\n }\n\n /** Same-origin check for state-changing requests (on top of SameSite=Strict cookies). */\n function sameOrigin(request: Request, url: URL): boolean {\n const origin = request.headers.get('origin');\n if (!origin) return true;\n return origin === url.origin || origin === publicOrigin;\n }\n\n function clientKey(request: Request): string {\n return (\n request.headers.get('cf-connecting-ip') ??\n request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ??\n 'local'\n );\n }\n\n async function readJson(request: Request): Promise<Record<string, unknown> | null> {\n if (!request.headers.get('content-type')?.includes('application/json')) return null;\n try {\n const body: unknown = await request.json();\n return body && typeof body === 'object' && !Array.isArray(body)\n ? (body as Record<string, unknown>)\n : null;\n } catch {\n return null;\n }\n }\n\n async function login(request: Request, url: URL): Promise<Response> {\n if (isOAuth) return json({ error: 'Use “Sign in with Google”.' }, 400);\n const limit = loginLimiter.check(clientKey(request));\n if (!limit.ok) {\n return json({ error: 'Too many attempts. Wait a minute and try again.' }, 429, {\n 'retry-after': String(limit.retryAfterSeconds),\n });\n }\n const body = await readJson(request);\n const token = typeof body?.token === 'string' ? body.token.trim() : '';\n if (!token || !(await timingSafeEqual(token, config.accessToken!))) {\n logger.warn('dashboard login failed');\n return json({ error: 'That access token is not correct.' }, 401);\n }\n const cookie = sessionCookie(await oauth.ownerSession(), {\n secure: isSecure(url),\n maxAge: SESSION_TTL,\n });\n return json({ ok: true }, 200, { 'set-cookie': cookie });\n }\n\n async function status(principal: Principal, request: Request, url: URL): Promise<Response> {\n const session = runtime.sessionFor(principal);\n const started = Date.now();\n let google: Record<string, unknown>;\n try {\n const properties = await listProperties(session.gsc, { fresh: true });\n google = { ok: true, latency_ms: Date.now() - started, properties };\n } catch (error) {\n const { message, kind } = publicErrorMessage(error);\n google = { ok: false, latency_ms: Date.now() - started, error: message, kind };\n }\n const latest = await latestRelease(deps.fetch);\n const base = deps.baseUrl(request, url);\n const warnings: string[] = [];\n if (!config.publicBaseUrl && !LOOPBACK.has(url.hostname)) {\n warnings.push(\n 'PUBLIC_BASE_URL is not set and could not be detected; connection snippets use this page’s address.',\n );\n }\n const g = config.googleOAuth;\n const oauthInfo = g\n ? {\n redirect_uri: `${base}/oauth/google/callback`,\n allowed_domains: g.allowedDomains,\n allowed_email_count: g.allowedEmails.length,\n open_to_anyone: g.allowedDomains.length === 0 && g.allowedEmails.length === 0,\n }\n : null;\n if (oauthInfo?.open_to_anyone) {\n warnings.push(\n 'Anyone with a Google account can sign in (each person only sees their own properties). Set ALLOWED_GOOGLE_EMAILS or ALLOWED_GOOGLE_DOMAINS to restrict access.',\n );\n }\n return json({\n version: VERSION,\n latest_version: latest,\n update_available: latest !== null && isNewer(latest, VERSION),\n platform: deps.platform,\n auth_mode: config.authMode,\n identity: session.gsc.identity,\n writes_enabled: config.allowWrites,\n secret_path_enabled: !isOAuth && config.secretPath,\n public_base_url: config.publicBaseUrl ?? null,\n rate_limit_per_minute: config.rateLimitPerMinute,\n activity_persistent: runtime.activity.persistent,\n oauth: oauthInfo,\n google,\n warnings,\n });\n }\n\n async function runTool(name: string, request: Request, principal: Principal): Promise<Response> {\n const session = runtime.sessionFor(principal);\n const tool = session.tools.find((t) => t.name === name && !t.write);\n if (!tool) return json({ error: `Unknown tool: ${name}` }, 404);\n const body = (await readJson(request)) ?? {};\n const parsed = tool.input.safeParse(body);\n if (!parsed.success) {\n const message = parsed.error.issues\n .map((i) => `${i.path.join('.')}: ${i.message}`)\n .join('; ');\n return json({ error: message }, 400);\n }\n try {\n return json({ result: await tool.run(session.ctx, parsed.data) });\n } catch (error) {\n const { message, kind } = publicErrorMessage(error);\n if (kind === 'internal') logger.error('dashboard tool failed', { tool: name, error });\n return json({ error: message, kind }, kind === 'internal' ? 500 : 400);\n }\n }\n\n return {\n async handle(request: Request, url: URL, path: string): Promise<Response> {\n const method = request.method;\n if (method !== 'GET' && method !== 'HEAD' && !sameOrigin(request, url)) {\n return json({ error: 'Cross-origin request rejected.' }, 403);\n }\n\n if (path === '/api/session') {\n if (method === 'GET') {\n const principal = await signedIn(request);\n return json({\n authenticated: principal !== null,\n auth_mode: config.authMode,\n login: isOAuth ? 'google' : 'token',\n email: principal?.email ?? null,\n });\n }\n if (method === 'POST') return login(request, url);\n if (method === 'DELETE') {\n return json({ ok: true }, 200, { 'set-cookie': clearedSessionCookie(isSecure(url)) });\n }\n return json({ error: 'Method not allowed' }, 405);\n }\n\n const principal = await signedIn(request);\n if (!principal) return json({ error: 'Sign in required.' }, 401);\n\n if (path === '/api/status' && method === 'GET') return status(principal, request, url);\n if (path === '/api/connect' && method === 'GET') {\n const base = deps.baseUrl(request, url);\n return json({\n base_url: base,\n mcp_url: `${base}/mcp`,\n auth_mode: config.authMode,\n secret_path_enabled: !isOAuth && config.secretPath,\n });\n }\n if (path === '/api/connect/secret' && method === 'GET') {\n return isOAuth\n ? json({ error: 'Not available in OAuth mode.' }, 404)\n : json({ access_token: config.accessToken });\n }\n if (path === '/api/activity' && method === 'GET') {\n // In OAuth mode each person only sees their own calls.\n const entries = runtime.activity\n .list()\n .filter((e) => !isOAuth || e.user === principal.email);\n return json({ persistent: runtime.activity.persistent, entries });\n }\n const toolMatch = /^\\/api\\/tools\\/([a-z_]+)$/.exec(path);\n if (toolMatch && method === 'POST') return runTool(toolMatch[1]!, request, principal);\n\n return json({ error: 'Not found' }, 404);\n },\n };\n}\n","// Stateless, tamper-proof tokens: JSON payloads encrypted with AES-256-GCM under keys derived (HKDF)\n// from one deployment secret, with a separate key per purpose. A sealed authorization code can\n// therefore never be replayed as an access token, a client ID, or a session.\nimport { base64ToBytes, base64UrlEncode, utf8 } from './crypto.js';\n\nexport type SealPurpose =\n 'client' | 'state' | 'request' | 'code' | 'access' | 'refresh' | 'session';\n\n/** Short visible prefixes make tokens recognisable in logs and support tickets (never their content). */\nexport const SEAL_PREFIX: Record<SealPurpose, string> = {\n client: 'gsc_client_',\n state: 'gsc_state_',\n request: 'gsc_req_',\n code: 'gsc_code_',\n access: 'gsc_at_',\n refresh: 'gsc_rt_',\n session: 'gsc_sess_',\n};\n\ntype AesKey = Awaited<ReturnType<typeof crypto.subtle.importKey>>;\n\nexport interface Sealer {\n seal(purpose: SealPurpose, payload: object, ttlSeconds: number): Promise<string>;\n /** Returns null for anything malformed, tampered with, of another purpose, or expired. */\n open<T extends object>(\n purpose: SealPurpose,\n token: string | null | undefined,\n ): Promise<(T & { exp: number }) | null>;\n}\n\nexport function createSealer(secret: string, now: () => number = Date.now): Sealer {\n const keys = new Map<SealPurpose, Promise<AesKey>>();\n\n const keyFor = (purpose: SealPurpose): Promise<AesKey> => {\n let key = keys.get(purpose);\n if (!key) {\n key = (async () => {\n const material = await crypto.subtle.importKey('raw', utf8(secret), 'HKDF', false, [\n 'deriveKey',\n ]);\n return crypto.subtle.deriveKey(\n {\n name: 'HKDF',\n hash: 'SHA-256',\n salt: utf8('gsclaw-seal-v1'),\n info: utf8(`gsclaw:${purpose}`),\n },\n material,\n { name: 'AES-GCM', length: 256 },\n false,\n ['encrypt', 'decrypt'],\n );\n })();\n keys.set(purpose, key);\n }\n return key;\n };\n\n return {\n async seal(purpose, payload, ttlSeconds) {\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const body = utf8(JSON.stringify({ ...payload, exp: Math.floor(now() / 1000) + ttlSeconds }));\n const cipher = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: 'AES-GCM', iv, additionalData: utf8(purpose) },\n await keyFor(purpose),\n body,\n ),\n );\n const out = new Uint8Array(iv.length + cipher.length);\n out.set(iv);\n out.set(cipher, iv.length);\n return SEAL_PREFIX[purpose] + base64UrlEncode(out);\n },\n\n async open<T extends object>(purpose: SealPurpose, token: string | null | undefined) {\n const prefix = SEAL_PREFIX[purpose];\n if (!token?.startsWith(prefix) || token.length > 8192) return null;\n try {\n const bytes = base64ToBytes(token.slice(prefix.length));\n if (bytes.length < 13 + 16) return null;\n const plain = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: bytes.subarray(0, 12), additionalData: utf8(purpose) },\n await keyFor(purpose),\n bytes.subarray(12),\n );\n const payload = JSON.parse(new TextDecoder().decode(plain)) as T & { exp: number };\n if (typeof payload.exp !== 'number' || payload.exp <= Math.floor(now() / 1000)) return null;\n return payload;\n } catch {\n return null;\n }\n },\n };\n}\n","import { VERSION } from './version.js';\n\n/** Strict CSP: the dashboard only loads its own bundle and talks to its own API. */\nexport const DASHBOARD_HEADERS: Record<string, string> = {\n 'content-type': 'text/html; charset=utf-8',\n 'content-security-policy':\n \"default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'; font-src 'self'; manifest-src 'self'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'\",\n 'x-content-type-options': 'nosniff',\n 'referrer-policy': 'no-referrer',\n 'x-frame-options': 'DENY',\n 'cache-control': 'no-store',\n};\n\n/**\n * The dashboard's HTML shell. It contains no data; the app behind it requires a session.\n * Assets have fixed names and are cache-busted with the version.\n */\nexport function dashboardShell(): string {\n const v = encodeURIComponent(VERSION);\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<meta name=\"robots\" content=\"noindex\">\n<meta name=\"color-scheme\" content=\"light dark\">\n<title>GSClaw</title>\n<link rel=\"icon\" type=\"image/svg+xml\" href=\"/assets/favicon.svg\">\n<link rel=\"stylesheet\" href=\"/assets/dashboard.css?v=${v}\">\n<script type=\"module\" src=\"/assets/dashboard.js?v=${v}\"></script>\n</head>\n<body>\n<div id=\"app\"><noscript>GSClaw's dashboard needs JavaScript.</noscript></div>\n</body>\n</html>`;\n}\n","// Compact, LLM-friendly formatting. Numbers are rounded, CTR is a percentage, and long result\n// sets are cut to the top N rows with an explicit note about what was left out.\n\nexport interface Metrics {\n clicks: number;\n impressions: number;\n /** Click-through rate in percent (0–100), rounded to 2 decimals. */\n ctr_pct: number;\n /** Average position (1 = top), rounded to 1 decimal. Null when there were no impressions. */\n position: number | null;\n}\n\nexport interface RawMetrics {\n clicks: number;\n impressions: number;\n ctr?: number;\n position?: number;\n}\n\nexport function round(value: number, decimals = 0): number {\n const f = 10 ** decimals;\n return Math.round(value * f) / f;\n}\n\nexport function toMetrics(raw: RawMetrics): Metrics {\n const ctr = raw.ctr ?? (raw.impressions > 0 ? raw.clicks / raw.impressions : 0);\n return {\n clicks: round(raw.clicks),\n impressions: round(raw.impressions),\n ctr_pct: round(ctr * 100, 2),\n position: raw.impressions > 0 && raw.position !== undefined ? round(raw.position, 1) : null,\n };\n}\n\n/** Sums rows into totals, with impression-weighted average position (how GSC aggregates). */\nexport function sumMetrics(rows: RawMetrics[]): Metrics {\n let clicks = 0;\n let impressions = 0;\n let weightedPosition = 0;\n for (const r of rows) {\n clicks += r.clicks;\n impressions += r.impressions;\n weightedPosition += (r.position ?? 0) * r.impressions;\n }\n return toMetrics({\n clicks,\n impressions,\n position: impressions > 0 ? weightedPosition / impressions : undefined,\n });\n}\n\nexport function fmtInt(value: number): string {\n return Math.round(value).toLocaleString('en-US');\n}\n\nexport function fmtPct(pct: number): string {\n const abs = Math.abs(pct);\n return `${abs > 0 && abs < 1 ? pct.toFixed(2) : pct.toFixed(1)}%`;\n}\n\nexport function fmtPos(position: number | null): string {\n return position === null ? '–' : position.toFixed(1);\n}\n\nexport function fmtSigned(value: number, format: (v: number) => string = fmtInt): string {\n if (value === 0) return format(0);\n return value > 0 ? `+${format(value)}` : `-${format(Math.abs(value))}`;\n}\n\n/** Relative change in percent, or null when the base is zero. */\nexport function pctChange(current: number, previous: number): number | null {\n if (previous === 0) return null;\n return round(((current - previous) / previous) * 100, 1);\n}\n\nexport function fmtChange(current: number, previous: number): string {\n const diff = current - previous;\n const rel = pctChange(current, previous);\n if (rel === null) return current === 0 ? '0' : `${fmtSigned(diff)} (new)`;\n return `${fmtSigned(diff)} (${fmtSigned(rel, (v) => `${v.toFixed(1)}%`)})`;\n}\n\nfunction cell(value: string | number | null | undefined): string {\n if (value === null || value === undefined) return '';\n return String(value).replace(/\\|/g, '\\\\|').replace(/\\s+/g, ' ');\n}\n\nexport function mdTable(headers: string[], rows: (string | number | null | undefined)[][]): string {\n if (rows.length === 0) return '_No rows._';\n const head = `| ${headers.join(' | ')} |`;\n const sep = `|${headers.map(() => '---').join('|')}|`;\n const body = rows.map((r) => `| ${r.map(cell).join(' | ')} |`);\n return [head, sep, ...body].join('\\n');\n}\n\nexport function omittedNote(shown: number, total: number, hint: string): string | null {\n if (total <= shown) return null;\n return `_${fmtInt(total - shown)} more row(s) not shown (${fmtInt(shown)} of ${fmtInt(total)}). ${hint}_`;\n}\n\n/**\n * Shortens a page URL for display: same-host URLs become their path. The full URL is always kept\n * in structured output.\n */\nexport function compactUrl(url: string, baseHost?: string): string {\n try {\n const u = new URL(url);\n const path = `${u.pathname}${u.search}${u.hash}`;\n if (baseHost && u.host === baseHost) return path;\n return `${u.host}${path}`;\n } catch {\n return url;\n }\n}\n\n/** Joins non-empty sections with blank lines. */\nexport function sections(...parts: (string | null | undefined | false)[]): string {\n return parts.filter((p): p is string => Boolean(p)).join('\\n\\n');\n}\n\n/** Hard cap so a single tool result never blows past client limits (claude.ai: ~150K chars). */\nexport const MAX_TEXT_CHARS = 60_000;\n\nexport function capText(text: string, max = MAX_TEXT_CHARS): string {\n if (text.length <= max) return text;\n return `${text.slice(0, max)}\\n\\n_[Output truncated at ${fmtInt(max)} characters. Narrow the query or lower \\`limit\\`.]_`;\n}\n","import { z } from 'zod';\n\nexport const seoHealthCheckArgs = z.object({\n site_url: z\n .string()\n .describe(\"Property to review, e.g. 'sc-domain:example.com' or 'example.com'.\"),\n date_range: z\n .enum(['last_28_days', 'last_3_months', 'last_6_months'])\n .optional()\n .describe('Analysis period (default last_28_days).'),\n brand_terms: z\n .string()\n .optional()\n .describe(\n \"Optional RE2 regex of brand queries to exclude from opportunity analysis, e.g. 'acme|acme corp'.\",\n ),\n});\n\nexport function seoHealthCheckPrompt(args: z.output<typeof seoHealthCheckArgs>): string {\n const range = args.date_range ?? 'last_28_days';\n const brand = args.brand_terms\n ? `Pass exclude_queries: \"${args.brand_terms}\" to the opportunity tools so brand searches don't skew results.`\n : 'If the site has obvious brand queries, mention that a brand-excluded view (exclude_queries) may change the picture.';\n return `Run a complete SEO health check of the Search Console property \"${args.site_url}\" using the GSClaw tools. Period: ${range}.\n\nWork through these steps, calling the tools yourself:\n1. list_sites: confirm the exact property and that we have access.\n2. compare_periods (dimension \"none\", then \"query\" and \"page\", compare_to \"previous_period\"): overall trend and the biggest movers. If the period allows, also check compare_to \"year_over_year\" to separate seasonality from real change.\n3. striking_distance_keywords: the best quick-win queries ranking ~8–20.\n4. ctr_opportunities: high-impression queries whose CTR is below expectation for their position (title/description fixes).\n5. keyword_cannibalization: queries split across several pages.\n6. content_decay: pages in steady decline.\n7. list_sitemaps: sitemap errors/warnings and whether Google reads them.\n8. page_report or inspect_url for the 2–3 most important pages you found (top losers or top striking-distance pages), to check index status and canonicals.\n${brand}\n\nThen write a concise report:\n- **Summary**: 3–5 bullets on overall health and trend (with numbers).\n- **Prioritized actions**: a numbered list ordered by expected impact vs effort. For each: the page/query, the evidence (numbers from the tools), and the concrete fix.\n- **Technical issues**: indexing, canonical or sitemap problems, if any.\n- **Watch list**: things to re-check in 2–4 weeks.\n\nRemember: Search Console data lags ~2–3 days, positions are averages, and rare queries are anonymized. Don't invent numbers; cite only what the tools returned.`;\n}\n","import { McpServer } from '@modelcontextprotocol/server';\nimport type { ActivityLog } from './activity.js';\nimport { publicErrorMessage } from './errors.js';\nimport { capText } from './format.js';\nimport type { Logger } from './log.js';\nimport { seoHealthCheckArgs, seoHealthCheckPrompt } from './prompts.js';\nimport type { AnyTool, ToolContext } from './tools/types.js';\nimport { NAME, VERSION } from './version.js';\n\nexport const SERVER_INSTRUCTIONS = `GSClaw gives you read access to Google Search Console (and sitemap writes only if the operator enabled them).\n- Start with list_sites to find the exact property; site_url also accepts a bare domain like \"example.com\".\n- Prefer the analysis tools (compare_periods, striking_distance_keywords, ctr_opportunities, keyword_cannibalization, content_decay, page_report) over raw search_analytics for common questions.\n- Dates are Pacific Time. Final data lags ~2–3 days; relative ranges end on the latest final day. Pass fresh=true for partial recent data.\n- Results show the top rows only and say how many were omitted; raise limit if you need more.\n- Search Console keeps ~16 months of data, and rare queries are anonymized.`;\n\nexport interface McpServerDeps {\n tools: AnyTool[];\n ctx: ToolContext;\n logger: Logger;\n client: string;\n /** Signed-in user's email (OAuth mode), recorded with each activity entry. */\n user?: string;\n activity?: ActivityLog;\n}\n\n/** Builds a fresh MCP server instance (one per HTTP request or stdio connection). */\nexport function buildMcpServer(deps: McpServerDeps): McpServer {\n const server = new McpServer(\n { name: NAME, title: 'GSClaw', version: VERSION },\n { instructions: SERVER_INSTRUCTIONS },\n );\n\n for (const tool of deps.tools) {\n server.registerTool(\n tool.name,\n {\n title: tool.title,\n description: tool.description,\n inputSchema: tool.input,\n annotations: { title: tool.title, ...tool.annotations },\n },\n async (args) => {\n const started = Date.now();\n const site =\n typeof args.site_url === 'string'\n ? args.site_url\n : typeof args.url === 'string'\n ? args.url\n : null;\n try {\n const result = await tool.run(deps.ctx, args);\n const text = capText(tool.format(result, args));\n deps.activity?.record({\n time: new Date().toISOString(),\n client: deps.client,\n tool: tool.name,\n site,\n ms: Date.now() - started,\n ok: true,\n user: deps.user,\n });\n return {\n content: [{ type: 'text', text }],\n structuredContent: result as Record<string, unknown>,\n };\n } catch (error) {\n const { message, kind } = publicErrorMessage(error);\n if (kind === 'internal') deps.logger.error('tool failed', { tool: tool.name, error });\n else deps.logger.info('tool returned error', { tool: tool.name, kind });\n deps.activity?.record({\n time: new Date().toISOString(),\n client: deps.client,\n tool: tool.name,\n site,\n ms: Date.now() - started,\n ok: false,\n error_kind: kind,\n user: deps.user,\n });\n return { content: [{ type: 'text', text: message }], isError: true };\n }\n },\n );\n }\n\n server.registerPrompt(\n 'seo_health_check',\n {\n title: 'SEO health check',\n description:\n 'Full review of a Search Console property: trends, quick wins, CTR fixes, cannibalization, decaying content, sitemaps and indexing, ending in a prioritized action plan.',\n argsSchema: seoHealthCheckArgs,\n },\n (args) => ({\n messages: [{ role: 'user', content: { type: 'text', text: seoHealthCheckPrompt(args) } }],\n }),\n );\n\n return server;\n}\n","interface Entry<V> {\n value: V;\n expiresAt: number;\n weight: number;\n}\n\n/**\n * Small in-memory LRU with TTL and a total weight budget (for example, number of rows held) so a\n * few huge responses cannot exhaust memory on small runtimes such as Cloudflare Workers.\n */\nexport class LruCache<V> {\n private readonly map = new Map<string, Entry<V>>();\n private totalWeight = 0;\n\n constructor(\n private readonly opts: { maxEntries: number; maxWeight: number; now?: () => number },\n ) {}\n\n private now(): number {\n return this.opts.now ? this.opts.now() : Date.now();\n }\n\n get(key: string): V | undefined {\n const entry = this.map.get(key);\n if (!entry) return undefined;\n if (entry.expiresAt <= this.now()) {\n this.delete(key);\n return undefined;\n }\n // Refresh recency.\n this.map.delete(key);\n this.map.set(key, entry);\n return entry.value;\n }\n\n set(key: string, value: V, ttlMs: number, weight = 1): void {\n if (ttlMs <= 0 || weight > this.opts.maxWeight) return;\n this.delete(key);\n this.map.set(key, { value, expiresAt: this.now() + ttlMs, weight });\n this.totalWeight += weight;\n while (this.map.size > this.opts.maxEntries || this.totalWeight > this.opts.maxWeight) {\n const oldest = this.map.keys().next().value;\n if (oldest === undefined) break;\n this.delete(oldest);\n }\n }\n\n delete(key: string): void {\n const entry = this.map.get(key);\n if (!entry) return;\n this.totalWeight -= entry.weight;\n this.map.delete(key);\n }\n\n deleteWhere(predicate: (key: string) => boolean): void {\n for (const key of [...this.map.keys()]) if (predicate(key)) this.delete(key);\n }\n\n get size(): number {\n return this.map.size;\n }\n}\n","import { LruCache } from '../cache.js';\nimport { GscApiError, toGscApiError } from '../errors.js';\nimport type { Logger } from '../log.js';\nimport type { TokenSource } from './auth.js';\nimport type {\n SearchAnalyticsRequest,\n SearchAnalyticsResponse,\n SearchAnalyticsRow,\n SiteEntry,\n Sitemap,\n UrlInspectionResponse,\n} from './types.js';\n\nexport const API_BASE = 'https://searchconsole.googleapis.com';\nconst WEBMASTERS = `${API_BASE}/webmasters/v3`;\nconst INSPECT_URL = `${API_BASE}/v1/urlInspection/index:inspect`;\n\n/** Google's maximum page size for searchanalytics.query. */\nexport const PAGE_SIZE = 25_000;\n\n/** Response cache; keys are scoped by identity, so users never see each other's data. */\nexport type ResponseCache = LruCache<unknown>;\n\n/** Weight budget is in rows, which keeps memory bounded on small runtimes (Workers: 128 MB). */\nexport function createResponseCache(): ResponseCache {\n return new LruCache<unknown>({ maxEntries: 300, maxWeight: 60_000 });\n}\n\nexport interface GscClientOptions {\n tokenSource: TokenSource;\n logger: Logger;\n fetch?: typeof fetch;\n cache?: ResponseCache;\n cacheTtlMs?: number;\n maxRetries?: number;\n timeoutMs?: number;\n sleep?: (ms: number) => Promise<void>;\n}\n\ninterface RequestOptions {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE';\n url: string;\n body?: unknown;\n siteUrl?: string;\n /** Cache weight; omit to skip caching. */\n cacheWeight?: (data: unknown) => number;\n /** Bypass the cached value (the fresh response is still cached). */\n fresh?: boolean;\n}\n\nconst RETRYABLE_STATUS = new Set([429, 500, 502, 503, 504]);\n\nexport class GscClient {\n private readonly tokenSource: TokenSource;\n private readonly logger: Logger;\n private readonly fetchImpl: typeof fetch;\n private readonly cache?: ResponseCache;\n private readonly cacheTtlMs: number;\n private readonly maxRetries: number;\n private readonly timeoutMs: number;\n private readonly sleep: (ms: number) => Promise<void>;\n\n constructor(opts: GscClientOptions) {\n this.tokenSource = opts.tokenSource;\n this.logger = opts.logger;\n this.fetchImpl = opts.fetch ?? ((input, init) => fetch(input, init));\n this.cache = opts.cache;\n this.cacheTtlMs = opts.cacheTtlMs ?? 300_000;\n this.maxRetries = opts.maxRetries ?? 3;\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n this.sleep = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));\n }\n\n get identity() {\n return this.tokenSource.identity;\n }\n\n get canWrite(): boolean {\n return this.tokenSource.scopes.includes('https://www.googleapis.com/auth/webmasters');\n }\n\n /** `fresh` skips the cache read (used by the dashboard's live connectivity check). */\n async listSites(opts: { fresh?: boolean } = {}): Promise<SiteEntry[]> {\n const data = await this.request<{ siteEntry?: SiteEntry[] }>({\n method: 'GET',\n url: `${WEBMASTERS}/sites`,\n cacheWeight: () => 1,\n fresh: opts.fresh,\n });\n return data.siteEntry ?? [];\n }\n\n async searchAnalytics(\n siteUrl: string,\n body: SearchAnalyticsRequest,\n ): Promise<SearchAnalyticsResponse> {\n return this.request<SearchAnalyticsResponse>({\n method: 'POST',\n url: `${WEBMASTERS}/sites/${encodeURIComponent(siteUrl)}/searchAnalytics/query`,\n body,\n siteUrl,\n cacheWeight: (d) => Math.max(1, (d as SearchAnalyticsResponse).rows?.length ?? 0),\n });\n }\n\n /**\n * Fetches up to `maxRows` rows, paging through `startRow` in 25,000-row pages. Google exposes at\n * most ~50K rows per day per search type, so very large sites may still be truncated upstream.\n */\n async searchAnalyticsAll(\n siteUrl: string,\n body: Omit<SearchAnalyticsRequest, 'rowLimit' | 'startRow'>,\n maxRows: number,\n ): Promise<{\n rows: SearchAnalyticsRow[];\n truncated: boolean;\n metadata?: SearchAnalyticsResponse['metadata'];\n }> {\n const rows: SearchAnalyticsRow[] = [];\n let metadata: SearchAnalyticsResponse['metadata'];\n let startRow = 0;\n for (;;) {\n const rowLimit = Math.min(PAGE_SIZE, maxRows - rows.length);\n const res = await this.searchAnalytics(siteUrl, { ...body, rowLimit, startRow });\n metadata ??= res.metadata;\n const page = res.rows ?? [];\n rows.push(...page);\n if (page.length < rowLimit) return { rows, truncated: false, metadata };\n if (rows.length >= maxRows) return { rows, truncated: true, metadata };\n startRow += page.length;\n }\n }\n\n async inspectUrl(\n siteUrl: string,\n inspectionUrl: string,\n languageCode = 'en-US',\n ): Promise<UrlInspectionResponse> {\n return this.request<UrlInspectionResponse>({\n method: 'POST',\n url: INSPECT_URL,\n body: { inspectionUrl, siteUrl, languageCode },\n siteUrl,\n cacheWeight: () => 1,\n });\n }\n\n async listSitemaps(siteUrl: string, sitemapIndex?: string): Promise<Sitemap[]> {\n const query = sitemapIndex ? `?sitemapIndex=${encodeURIComponent(sitemapIndex)}` : '';\n const data = await this.request<{ sitemap?: Sitemap[] }>({\n method: 'GET',\n url: `${WEBMASTERS}/sites/${encodeURIComponent(siteUrl)}/sitemaps${query}`,\n siteUrl,\n cacheWeight: () => 1,\n });\n return data.sitemap ?? [];\n }\n\n async getSitemap(siteUrl: string, feedpath: string): Promise<Sitemap> {\n return this.request<Sitemap>({\n method: 'GET',\n url: `${WEBMASTERS}/sites/${encodeURIComponent(siteUrl)}/sitemaps/${encodeURIComponent(feedpath)}`,\n siteUrl,\n cacheWeight: () => 1,\n });\n }\n\n async submitSitemap(siteUrl: string, feedpath: string): Promise<void> {\n await this.request({\n method: 'PUT',\n url: `${WEBMASTERS}/sites/${encodeURIComponent(siteUrl)}/sitemaps/${encodeURIComponent(feedpath)}`,\n siteUrl,\n });\n this.invalidateSite(siteUrl);\n }\n\n async deleteSitemap(siteUrl: string, feedpath: string): Promise<void> {\n await this.request({\n method: 'DELETE',\n url: `${WEBMASTERS}/sites/${encodeURIComponent(siteUrl)}/sitemaps/${encodeURIComponent(feedpath)}`,\n siteUrl,\n });\n this.invalidateSite(siteUrl);\n }\n\n private cacheKey(opts: RequestOptions): string {\n return `${this.identity.email}\\n${opts.method} ${opts.url}\\n${opts.body ? JSON.stringify(opts.body) : ''}`;\n }\n\n private invalidateSite(siteUrl: string): void {\n const marker = encodeURIComponent(siteUrl);\n this.cache?.deleteWhere(\n (key) => key.startsWith(`${this.identity.email}\\n`) && key.includes(marker),\n );\n }\n\n private async request<T>(opts: RequestOptions): Promise<T> {\n const useCache = this.cache && opts.cacheWeight && this.cacheTtlMs > 0;\n const key = useCache ? this.cacheKey(opts) : '';\n if (useCache && !opts.fresh) {\n const hit = this.cache.get(key);\n if (hit !== undefined) {\n this.logger.debug('gsc cache hit', { method: opts.method, path: pathOf(opts.url) });\n return hit as T;\n }\n }\n\n let refreshedToken = false;\n for (let attempt = 0; ; attempt++) {\n const started = Date.now();\n let res: Response;\n try {\n const token = await this.tokenSource.getAccessToken();\n res = await this.fetchImpl(opts.url, {\n method: opts.method,\n headers: {\n authorization: `Bearer ${token}`,\n ...(opts.body !== undefined ? { 'content-type': 'application/json' } : {}),\n },\n body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n } catch (error) {\n if (error instanceof GscApiError) {\n if (error.retryable && attempt < this.maxRetries) {\n await this.sleep(backoff(attempt));\n continue;\n }\n throw error;\n }\n if (attempt < this.maxRetries) {\n this.logger.warn('gsc network error, retrying', { attempt, error });\n await this.sleep(backoff(attempt));\n continue;\n }\n throw new GscApiError({\n kind: 'network',\n status: 0,\n googleMessage: '',\n retryable: true,\n advice:\n 'Could not reach the Google Search Console API (network error or timeout). Try again shortly.',\n });\n }\n\n this.logger.debug('gsc request', {\n method: opts.method,\n path: pathOf(opts.url),\n status: res.status,\n ms: Date.now() - started,\n });\n\n if (res.ok) {\n const text = await res.text();\n const data = (text ? JSON.parse(text) : {}) as T;\n if (useCache) this.cache.set(key, data, this.cacheTtlMs, opts.cacheWeight!(data));\n return data;\n }\n\n const body: unknown = await res.json().catch(() => undefined);\n if (res.status === 401 && !refreshedToken) {\n refreshedToken = true;\n this.tokenSource.invalidate();\n continue;\n }\n const error = toGscApiError(res.status, body, {\n identity: this.identity,\n siteUrl: opts.siteUrl,\n });\n const retryable =\n RETRYABLE_STATUS.has(res.status) || (error.kind === 'rate_limited' && error.retryable);\n if (retryable && attempt < this.maxRetries) {\n const retryAfter = Number(res.headers.get('retry-after'));\n const delay =\n Number.isFinite(retryAfter) && retryAfter > 0\n ? Math.min(retryAfter * 1000, 10_000)\n : backoff(attempt);\n this.logger.info('gsc retrying', { status: res.status, attempt, delay });\n await this.sleep(delay);\n continue;\n }\n throw error;\n }\n }\n}\n\n/** Exponential backoff with jitter: ~0.5s, 1s, 2s, 4s (+ up to 250ms). */\nexport function backoff(attempt: number): number {\n return 500 * 2 ** attempt + Math.floor(Math.random() * 250);\n}\n\n/** Path for logs, with the (possibly sensitive) site URL segment left encoded. */\nfunction pathOf(url: string): string {\n return url.replace(API_BASE, '');\n}\n","import { pctChange, round, toMetrics, type Metrics, type RawMetrics } from '../format.js';\n\nexport interface MetricChange {\n clicks: number;\n clicks_pct: number | null;\n impressions: number;\n impressions_pct: number | null;\n /** CTR change in percentage points. */\n ctr_pp: number;\n /** Positive = ranks higher than before (average position number went down). */\n rank_improvement: number | null;\n}\n\nexport interface ComparedRow {\n key: string;\n current: Metrics;\n previous: Metrics;\n change: MetricChange;\n status: 'new' | 'lost' | 'both';\n}\n\nconst EMPTY: RawMetrics = { clicks: 0, impressions: 0 };\n\nexport function metricChange(current: Metrics, previous: Metrics): MetricChange {\n return {\n clicks: current.clicks - previous.clicks,\n clicks_pct: pctChange(current.clicks, previous.clicks),\n impressions: current.impressions - previous.impressions,\n impressions_pct: pctChange(current.impressions, previous.impressions),\n ctr_pp: round(current.ctr_pct - previous.ctr_pct, 2),\n rank_improvement:\n current.position !== null && previous.position !== null\n ? round(previous.position - current.position, 1)\n : null,\n };\n}\n\n/** Full outer join of two keyed row sets with per-key deltas. */\nexport function compareRows(\n current: Map<string, RawMetrics>,\n previous: Map<string, RawMetrics>,\n): ComparedRow[] {\n const keys = new Set([...current.keys(), ...previous.keys()]);\n const rows: ComparedRow[] = [];\n for (const key of keys) {\n const cur = toMetrics(current.get(key) ?? EMPTY);\n const prev = toMetrics(previous.get(key) ?? EMPTY);\n rows.push({\n key,\n current: cur,\n previous: prev,\n change: metricChange(cur, prev),\n status: !previous.has(key) ? 'new' : !current.has(key) ? 'lost' : 'both',\n });\n }\n return rows;\n}\n\nexport function topGainers(rows: ComparedRow[], n: number): ComparedRow[] {\n return rows\n .filter((r) => r.change.clicks > 0)\n .sort(\n (a, b) => b.change.clicks - a.change.clicks || b.change.impressions - a.change.impressions,\n )\n .slice(0, n);\n}\n\nexport function topLosers(rows: ComparedRow[], n: number): ComparedRow[] {\n return rows\n .filter((r) => r.change.clicks < 0)\n .sort(\n (a, b) => a.change.clicks - b.change.clicks || a.change.impressions - b.change.impressions,\n )\n .slice(0, n);\n}\n\nexport function topByCurrentClicks(rows: ComparedRow[], n: number): ComparedRow[] {\n return [...rows]\n .filter((r) => r.status !== 'lost')\n .sort(\n (a, b) =>\n b.current.clicks - a.current.clicks || b.current.impressions - a.current.impressions,\n )\n .slice(0, n);\n}\n","// Search Console reports dates in Pacific Time, so every relative preset is resolved in PT.\n// All dates are plain YYYY-MM-DD strings; arithmetic is done in UTC to avoid DST surprises.\n\nexport const DATE_PRESETS = [\n 'last_7_days',\n 'last_28_days',\n 'last_3_months',\n 'last_6_months',\n 'last_12_months',\n 'last_16_months',\n 'previous_period',\n] as const;\nexport type DatePreset = (typeof DATE_PRESETS)[number];\n\nexport const COMPARE_MODES = ['previous_period', 'year_over_year'] as const;\nexport type CompareMode = (typeof COMPARE_MODES)[number];\n\nexport interface DateRange {\n startDate: string;\n endDate: string;\n}\n\n/** Search Console keeps roughly 16 months of performance data. */\nexport const RETENTION_MONTHS = 16;\n/** Final (non-fresh) data typically lags by 2–3 days. */\nexport const DEFAULT_LAG_DAYS = 3;\n\nconst DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nexport function isValidDate(value: string): boolean {\n if (!DATE_RE.test(value)) return false;\n const d = new Date(`${value}T00:00:00Z`);\n return !Number.isNaN(d.getTime()) && d.toISOString().slice(0, 10) === value;\n}\n\nconst ptFormatter = new Intl.DateTimeFormat('en-CA', {\n timeZone: 'America/Los_Angeles',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n});\n\nexport function todayPT(now: Date = new Date()): string {\n return ptFormatter.format(now);\n}\n\nfunction toUtc(date: string): Date {\n return new Date(`${date}T00:00:00Z`);\n}\n\nfunction fromUtc(d: Date): string {\n return d.toISOString().slice(0, 10);\n}\n\nexport function addDays(date: string, days: number): string {\n const d = toUtc(date);\n d.setUTCDate(d.getUTCDate() + days);\n return fromUtc(d);\n}\n\n/** Calendar month arithmetic that clamps to the end of shorter months (Mar 31 − 1 month = Feb 28). */\nexport function addMonths(date: string, months: number): string {\n const d = toUtc(date);\n const day = d.getUTCDate();\n d.setUTCDate(1);\n d.setUTCMonth(d.getUTCMonth() + months);\n const lastDay = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + 1, 0)).getUTCDate();\n d.setUTCDate(Math.min(day, lastDay));\n return fromUtc(d);\n}\n\n/** Inclusive number of days in a range. */\nexport function rangeLength(range: DateRange): number {\n return (\n Math.round((toUtc(range.endDate).getTime() - toUtc(range.startDate).getTime()) / 86_400_000) + 1\n );\n}\n\nexport function resolvePreset(preset: DatePreset, endDate: string): DateRange {\n const lastDays = (n: number): DateRange => ({ startDate: addDays(endDate, -(n - 1)), endDate });\n const lastMonths = (n: number): DateRange => ({\n startDate: addDays(addMonths(endDate, -n), 1),\n endDate,\n });\n switch (preset) {\n case 'last_7_days':\n return lastDays(7);\n case 'last_28_days':\n return lastDays(28);\n case 'last_3_months':\n return lastMonths(3);\n case 'last_6_months':\n return lastMonths(6);\n case 'last_12_months':\n return lastMonths(12);\n case 'last_16_months':\n return lastMonths(RETENTION_MONTHS);\n case 'previous_period':\n return previousPeriod(lastDays(28));\n }\n}\n\n/** The range of equal length immediately before `range`. */\nexport function previousPeriod(range: DateRange): DateRange {\n const length = rangeLength(range);\n const endDate = addDays(range.startDate, -1);\n return { startDate: addDays(endDate, -(length - 1)), endDate };\n}\n\n/** The same calendar dates one year earlier. */\nexport function yearOverYear(range: DateRange): DateRange {\n return { startDate: addMonths(range.startDate, -12), endDate: addMonths(range.endDate, -12) };\n}\n\nexport function comparisonRange(range: DateRange, mode: CompareMode): DateRange {\n return mode === 'year_over_year' ? yearOverYear(range) : previousPeriod(range);\n}\n\n/** Oldest date Search Console still has data for, given today's date (PT). */\nexport function earliestAvailableDate(today: string): string {\n return addDays(addMonths(today, -RETENTION_MONTHS), 1);\n}\n\n/** Splits a range into `count` consecutive, equal-length windows ending at `endDate`. */\nexport function consecutiveWindows(endDate: string, count: number, days: number): DateRange[] {\n const windows: DateRange[] = [];\n let end = endDate;\n for (let i = 0; i < count; i++) {\n const start = addDays(end, -(days - 1));\n windows.unshift({ startDate: start, endDate: end });\n end = addDays(start, -1);\n }\n return windows;\n}\n","// Raw Google Search Console API shapes (webmasters/v3 + searchconsole/v1).\n\nexport const SEARCH_TYPES = ['web', 'image', 'video', 'news', 'discover', 'googleNews'] as const;\nexport type SearchType = (typeof SEARCH_TYPES)[number];\n\nexport const DIMENSIONS = [\n 'query',\n 'page',\n 'country',\n 'device',\n 'date',\n 'searchAppearance',\n 'hour',\n] as const;\nexport type Dimension = (typeof DIMENSIONS)[number];\n\nexport const FILTER_DIMENSIONS = [\n 'query',\n 'page',\n 'country',\n 'device',\n 'searchAppearance',\n] as const;\nexport type FilterDimension = (typeof FILTER_DIMENSIONS)[number];\n\nexport const FILTER_OPERATORS = [\n 'equals',\n 'notEquals',\n 'contains',\n 'notContains',\n 'includingRegex',\n 'excludingRegex',\n] as const;\nexport type FilterOperator = (typeof FILTER_OPERATORS)[number];\n\nexport const AGGREGATION_TYPES = ['auto', 'byPage', 'byProperty', 'byNewsShowcasePanel'] as const;\nexport type AggregationType = (typeof AGGREGATION_TYPES)[number];\n\nexport const DATA_STATES = ['final', 'all', 'hourly_all'] as const;\nexport type DataState = (typeof DATA_STATES)[number];\n\nexport interface SiteEntry {\n siteUrl: string;\n permissionLevel: string;\n}\n\nexport interface DimensionFilter {\n dimension: FilterDimension;\n operator?: FilterOperator;\n expression: string;\n}\n\nexport interface SearchAnalyticsRequest {\n startDate: string;\n endDate: string;\n dimensions?: Dimension[];\n type?: SearchType;\n dimensionFilterGroups?: { groupType?: 'and'; filters: DimensionFilter[] }[];\n aggregationType?: AggregationType;\n rowLimit?: number;\n startRow?: number;\n dataState?: DataState;\n}\n\nexport interface SearchAnalyticsRow {\n keys?: string[];\n clicks: number;\n impressions: number;\n ctr: number;\n position: number;\n}\n\nexport interface SearchAnalyticsResponse {\n rows?: SearchAnalyticsRow[];\n responseAggregationType?: string;\n metadata?: {\n first_incomplete_date?: string;\n first_incomplete_hour?: string;\n firstIncompleteDate?: string;\n firstIncompleteHour?: string;\n };\n}\n\nexport interface InspectionIssue {\n issueMessage?: string;\n message?: string;\n issueType?: string;\n severity?: string;\n}\n\nexport interface UrlInspectionResult {\n inspectionResultLink?: string;\n indexStatusResult?: {\n verdict?: string;\n coverageState?: string;\n robotsTxtState?: string;\n indexingState?: string;\n lastCrawlTime?: string;\n pageFetchState?: string;\n googleCanonical?: string;\n userCanonical?: string;\n sitemap?: string[];\n referringUrls?: string[];\n crawledAs?: string;\n };\n mobileUsabilityResult?: { verdict?: string; issues?: InspectionIssue[] };\n richResultsResult?: {\n verdict?: string;\n detectedItems?: {\n richResultType?: string;\n items?: { name?: string; issues?: InspectionIssue[] }[];\n }[];\n };\n ampResult?: {\n verdict?: string;\n ampUrl?: string;\n indexingState?: string;\n ampIndexStatusVerdict?: string;\n issues?: InspectionIssue[];\n };\n}\n\nexport interface UrlInspectionResponse {\n inspectionResult?: UrlInspectionResult;\n}\n\nexport interface Sitemap {\n path: string;\n lastSubmitted?: string;\n isPending?: boolean;\n isSitemapsIndex?: boolean;\n type?: string;\n lastDownloaded?: string;\n /** int64 fields arrive as strings. */\n warnings?: string | number;\n errors?: string | number;\n contents?: { type?: string; submitted?: string | number; indexed?: string | number }[];\n}\n\nexport interface GoogleErrorBody {\n error?: {\n code?: number;\n message?: string;\n status?: string;\n errors?: { reason?: string; message?: string; domain?: string }[];\n details?: { '@type'?: string; reason?: string; metadata?: Record<string, string> }[];\n };\n}\n","import { z } from 'zod';\nimport {\n addDays,\n DATE_PRESETS,\n DEFAULT_LAG_DAYS,\n earliestAvailableDate,\n isValidDate,\n resolvePreset,\n todayPT,\n type DatePreset,\n type DateRange,\n} from '../dates.js';\nimport { UserFacingError } from '../errors.js';\nimport type { RawMetrics } from '../format.js';\nimport {\n FILTER_DIMENSIONS,\n FILTER_OPERATORS,\n SEARCH_TYPES,\n type Dimension,\n type DimensionFilter,\n type SearchAnalyticsRow,\n type SearchType,\n} from '../gsc/types.js';\nimport { resolveProperty, type Property } from '../gsc/sites.js';\nimport type { ToolContext } from './types.js';\n\nexport const siteUrlField = z\n .string()\n .min(1)\n .describe(\n \"Search Console property: 'sc-domain:example.com', 'https://www.example.com/', or just 'example.com' (matched against your properties; see list_sites).\",\n );\n\nconst dateField = z.string().refine(isValidDate, 'Use YYYY-MM-DD.').optional();\n\nexport const dateRangeFields = {\n date_range: z\n .enum(DATE_PRESETS)\n .default('last_28_days')\n .describe(\n 'Relative period ending on the latest day with final data (Search Console lags ~2–3 days). previous_period = the 28 days before last_28_days.',\n ),\n start_date: dateField.describe(\n 'Explicit start date YYYY-MM-DD (Pacific Time). Use together with end_date; overrides date_range.',\n ),\n end_date: dateField.describe('Explicit end date YYYY-MM-DD (Pacific Time), inclusive.'),\n fresh: z\n .boolean()\n .default(false)\n .describe(\n 'Include fresh, still-changing data from the most recent ~2 days (dataState \"all\"). Default: final data only.',\n ),\n};\n\nexport const searchTypeField = z\n .enum(SEARCH_TYPES)\n .default('web')\n .describe('Search type. Discover and Google News have no query or position data.');\n\nexport const filterSchema = z.object({\n dimension: z.enum(FILTER_DIMENSIONS),\n operator: z\n .enum(FILTER_OPERATORS)\n .default('equals')\n .describe(\n 'equals/notEquals are case-sensitive; contains/notContains are not; regex uses RE2 syntax.',\n ),\n expression: z\n .string()\n .min(1)\n .max(4096)\n .describe(\n 'Value to match. Countries are ISO 3166-1 alpha-3 (e.g. USA); devices are DESKTOP, MOBILE, TABLET.',\n ),\n});\n\nexport const filtersField = z\n .array(filterSchema)\n .max(20)\n .optional()\n .describe('Filters, all ANDed together.');\n\nexport function limitField(defaultValue: number, max = 500) {\n return z\n .number()\n .int()\n .min(1)\n .max(max)\n .default(defaultValue)\n .describe(\n `How many rows to show (top N, default ${defaultValue}). Results always say how many more exist.`,\n );\n}\n\nexport interface DateInput {\n date_range: DatePreset;\n start_date?: string;\n end_date?: string;\n fresh: boolean;\n}\n\nexport interface ResolvedWindow extends DateRange {\n dataState: 'final' | 'all';\n label: string;\n}\n\n/**\n * Latest date that already has final data, found with a cheap date-grouped query (cached by the\n * client). Falls back to today − 3 days for properties with no recent data.\n */\nexport async function latestFinalDate(\n ctx: ToolContext,\n siteUrl: string,\n searchType: SearchType,\n): Promise<string> {\n const today = todayPT(ctx.now());\n const res = await ctx.gsc.searchAnalytics(siteUrl, {\n startDate: addDays(today, -10),\n endDate: today,\n dimensions: ['date'],\n type: searchType,\n dataState: 'final',\n rowLimit: 15,\n });\n const dates = (res.rows ?? []).map((r) => r.keys?.[0]).filter((d): d is string => Boolean(d));\n return dates.length > 0 ? dates.sort().at(-1)! : addDays(today, -DEFAULT_LAG_DAYS);\n}\n\nexport async function resolveWindow(\n ctx: ToolContext,\n property: Property,\n input: DateInput,\n searchType: SearchType = 'web',\n): Promise<ResolvedWindow> {\n const today = todayPT(ctx.now());\n const dataState = input.fresh ? 'all' : 'final';\n\n if (input.start_date || input.end_date) {\n if (!input.start_date || !input.end_date) {\n throw new UserFacingError(\n 'Provide both start_date and end_date, or neither (to use date_range).',\n );\n }\n if (input.start_date > input.end_date)\n throw new UserFacingError('start_date must be on or before end_date.');\n if (input.end_date > today)\n throw new UserFacingError(\n `end_date cannot be in the future (today is ${today} Pacific Time).`,\n );\n const earliest = earliestAvailableDate(today);\n if (input.end_date < earliest) {\n throw new UserFacingError(\n `Search Console only keeps ~16 months of data; the earliest available date is about ${earliest}.`,\n );\n }\n return {\n startDate: input.start_date < earliest ? earliest : input.start_date,\n endDate: input.end_date,\n dataState,\n label: `${input.start_date} → ${input.end_date}`,\n };\n }\n\n const endDate = input.fresh ? today : await latestFinalDate(ctx, property.site_url, searchType);\n const range = resolvePreset(input.date_range, endDate);\n const earliest = earliestAvailableDate(today);\n return {\n ...range,\n startDate: range.startDate < earliest ? earliest : range.startDate,\n dataState,\n label: input.date_range,\n };\n}\n\nexport function windowNote(window: ResolvedWindow): string {\n const state =\n window.dataState === 'all'\n ? 'includes fresh data; the last ~2 days are incomplete and will still change'\n : 'final data only; Search Console lags ~2–3 days';\n return `_${window.startDate} → ${window.endDate} (Pacific Time, ${state})._`;\n}\n\nexport function toFilterGroups(filters: DimensionFilter[] | undefined) {\n if (!filters || filters.length === 0) return undefined;\n return [{ groupType: 'and' as const, filters }];\n}\n\nexport interface FetchOptions {\n type?: SearchType;\n filters?: DimensionFilter[];\n maxRows?: number;\n dataState?: 'final' | 'all';\n}\n\n/** All rows for a window and dimension set (auto-paginated, capped by GSCLAW_MAX_ROWS). */\nexport async function fetchRows(\n ctx: ToolContext,\n siteUrl: string,\n range: DateRange,\n dimensions: Dimension[],\n opts: FetchOptions = {},\n): Promise<{ rows: SearchAnalyticsRow[]; truncated: boolean }> {\n return ctx.gsc.searchAnalyticsAll(\n siteUrl,\n {\n startDate: range.startDate,\n endDate: range.endDate,\n dimensions,\n type: opts.type ?? 'web',\n dimensionFilterGroups: toFilterGroups(opts.filters),\n dataState: opts.dataState ?? 'final',\n },\n Math.min(opts.maxRows ?? ctx.config.maxRows, ctx.config.maxRows),\n );\n}\n\n/** Property totals for a window (a single dimensionless row). */\nexport async function fetchTotals(\n ctx: ToolContext,\n siteUrl: string,\n range: DateRange,\n opts: FetchOptions = {},\n): Promise<RawMetrics> {\n const res = await ctx.gsc.searchAnalytics(siteUrl, {\n startDate: range.startDate,\n endDate: range.endDate,\n type: opts.type ?? 'web',\n dimensionFilterGroups: toFilterGroups(opts.filters),\n dataState: opts.dataState ?? 'final',\n rowLimit: 1,\n });\n return res.rows?.[0] ?? { clicks: 0, impressions: 0 };\n}\n\n/** Keyed rows for one dimension, e.g. query → metrics. */\nexport async function fetchKeyed(\n ctx: ToolContext,\n siteUrl: string,\n range: DateRange,\n dimension: Dimension,\n opts: FetchOptions = {},\n): Promise<{ rows: Map<string, RawMetrics>; truncated: boolean }> {\n const { rows, truncated } = await fetchRows(ctx, siteUrl, range, [dimension], opts);\n return { rows: new Map(rows.map((r) => [r.keys?.[0] ?? '', r])), truncated };\n}\n\n/** Adds an excludingRegex query filter when a pattern is given (e.g. to drop branded queries). */\nexport function withQueryExclusion(\n filters: DimensionFilter[] | undefined,\n pattern: string | undefined,\n): DimensionFilter[] | undefined {\n if (!pattern) return filters;\n return [\n ...(filters ?? []),\n { dimension: 'query', operator: 'excludingRegex', expression: pattern },\n ];\n}\n\nexport const excludeQueriesField = z\n .string()\n .max(4096)\n .optional()\n .describe(\"RE2 regex of queries to exclude, e.g. your brand: 'acme|acme corp'.\");\n\nexport async function propertyFor(ctx: ToolContext, siteUrl: string): Promise<Property> {\n return resolveProperty(ctx.gsc, siteUrl);\n}\n\n/** Host used to shorten page URLs in text output (URL-prefix properties only). */\nexport function displayHost(siteUrl: string): string | undefined {\n if (siteUrl.startsWith('sc-domain:')) return undefined;\n try {\n return new URL(siteUrl).host;\n } catch {\n return undefined;\n }\n}\n\n/** Strips the #fragment Google sometimes reports for jump links and sitelinks. */\nexport function stripFragment(url: string): string {\n const i = url.indexOf('#');\n return i === -1 ? url : url.slice(0, i);\n}\n","import type { z } from 'zod';\nimport type { Config } from '../config.js';\nimport type { GscClient } from '../gsc/client.js';\nimport type { Logger } from '../log.js';\n\nexport interface ToolContext {\n gsc: GscClient;\n config: Pick<Config, 'maxRows' | 'batchInspectMax' | 'allowWrites'>;\n logger: Logger;\n now: () => Date;\n}\n\nexport interface ToolAnnotations {\n readOnlyHint: boolean;\n destructiveHint?: boolean;\n idempotentHint?: boolean;\n openWorldHint: boolean;\n}\n\n/**\n * One tool, defined once. MCP serves `format(run(...))` as text plus the raw result as structured\n * content; the dashboard API calls the very same `run`, so both always see identical numbers.\n */\nexport interface ToolDefinition<S extends z.ZodObject = z.ZodObject, R = unknown> {\n name: string;\n title: string;\n description: string;\n input: S;\n annotations: ToolAnnotations;\n /** Mutating tools are only registered when GSCLAW_ALLOW_WRITES=true and the credentials can write. */\n write?: boolean;\n run(ctx: ToolContext, input: z.output<S>): Promise<R>;\n format(result: R, input: z.output<S>): string;\n}\n\nexport type AnyTool = ToolDefinition<z.ZodObject, unknown>;\n\nexport function defineTool<S extends z.ZodObject, R>(\n def: ToolDefinition<S, R>,\n): ToolDefinition<S, R> {\n return def;\n}\n\nexport const READ_ONLY: ToolAnnotations = {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n};\n","import { z } from 'zod';\nimport {\n compareRows,\n metricChange,\n topByCurrentClicks,\n topGainers,\n topLosers,\n type ComparedRow,\n type MetricChange,\n} from '../analysis/compare.js';\nimport {\n COMPARE_MODES,\n comparisonRange,\n earliestAvailableDate,\n todayPT,\n type CompareMode,\n type DateRange,\n} from '../dates.js';\nimport { UserFacingError } from '../errors.js';\nimport {\n compactUrl,\n fmtChange,\n fmtInt,\n fmtPct,\n fmtPos,\n fmtSigned,\n mdTable,\n sections,\n toMetrics,\n type Metrics,\n} from '../format.js';\nimport {\n dateRangeFields,\n displayHost,\n fetchKeyed,\n fetchTotals,\n filtersField,\n limitField,\n propertyFor,\n resolveWindow,\n searchTypeField,\n siteUrlField,\n} from './common.js';\nimport { defineTool, READ_ONLY } from './types.js';\n\nexport const COMPARE_DIMENSIONS = [\n 'query',\n 'page',\n 'country',\n 'device',\n 'searchAppearance',\n 'none',\n] as const;\nexport const COMPARE_VIEWS = ['movers', 'top', 'all'] as const;\n\nexport interface ComparePeriodsResult {\n site_url: string;\n dimension: (typeof COMPARE_DIMENSIONS)[number];\n compare_to: CompareMode;\n current: DateRange;\n previous: DateRange;\n data_state: 'final' | 'all';\n totals: { current: Metrics; previous: Metrics; change: MetricChange };\n /** Top rows by current-period clicks, with deltas (view = top | all). */\n top: ComparedRow[];\n gainers: ComparedRow[];\n losers: ComparedRow[];\n new_count: number;\n lost_count: number;\n compared_count: number;\n truncated: boolean;\n}\n\nexport const comparePeriods = defineTool({\n name: 'compare_periods',\n title: 'Compare periods (gainers & losers)',\n description:\n 'Compares a period with the previous period or the same dates last year: property totals with deltas, plus the top gaining and losing queries/pages/countries/devices by clicks. ' +\n 'Use for \"what changed?\", traffic drops, and month-over-month or year-over-year reports. dimension \"none\" returns totals only.',\n input: z.object({\n site_url: siteUrlField,\n ...dateRangeFields,\n compare_to: z\n .enum(COMPARE_MODES)\n .default('previous_period')\n .describe(\n 'previous_period (same length, right before) or year_over_year (same dates last year).',\n ),\n dimension: z.enum(COMPARE_DIMENSIONS).default('query'),\n view: z\n .enum(COMPARE_VIEWS)\n .default('movers')\n .describe('movers = top gainers + losers; top = biggest rows now with deltas; all = both.'),\n filters: filtersField,\n search_type: searchTypeField,\n limit: limitField(10, 100),\n }),\n annotations: READ_ONLY,\n async run(ctx, input): Promise<ComparePeriodsResult> {\n const property = await propertyFor(ctx, input.site_url);\n const current = await resolveWindow(ctx, property, input, input.search_type);\n const previous = comparisonRange(current, input.compare_to);\n const earliest = earliestAvailableDate(todayPT(ctx.now()));\n if (previous.startDate < earliest) {\n throw new UserFacingError(\n `The comparison period would start ${previous.startDate}, but Search Console only keeps ~16 months of data (earliest ≈ ${earliest}). Use a shorter date_range or compare_to \"previous_period\".`,\n );\n }\n const opts = { type: input.search_type, filters: input.filters, dataState: current.dataState };\n const dimension = input.dimension === 'none' ? null : input.dimension;\n const [curTotals, prevTotals, curRows, prevRows] = await Promise.all([\n fetchTotals(ctx, property.site_url, current, opts),\n fetchTotals(ctx, property.site_url, previous, opts),\n dimension ? fetchKeyed(ctx, property.site_url, current, dimension, opts) : null,\n dimension ? fetchKeyed(ctx, property.site_url, previous, dimension, opts) : null,\n ]);\n const rows = curRows && prevRows ? compareRows(curRows.rows, prevRows.rows) : [];\n const cur = toMetrics(curTotals);\n const prev = toMetrics(prevTotals);\n const wantTop = input.view !== 'movers';\n const wantMovers = input.view !== 'top';\n return {\n site_url: property.site_url,\n dimension: input.dimension,\n compare_to: input.compare_to,\n current: { startDate: current.startDate, endDate: current.endDate },\n previous,\n data_state: current.dataState,\n totals: { current: cur, previous: prev, change: metricChange(cur, prev) },\n top: wantTop ? topByCurrentClicks(rows, input.limit) : [],\n gainers: wantMovers ? topGainers(rows, input.limit) : [],\n losers: wantMovers ? topLosers(rows, input.limit) : [],\n new_count: rows.filter((r) => r.status === 'new').length,\n lost_count: rows.filter((r) => r.status === 'lost').length,\n compared_count: rows.length,\n truncated: Boolean(curRows?.truncated || prevRows?.truncated),\n };\n },\n format(r) {\n const t = r.totals;\n const host = displayHost(r.site_url);\n const label = (key: string) => (r.dimension === 'page' ? compactUrl(key, host) : key);\n const totals = mdTable(\n ['Metric', 'Current', 'Previous', 'Change'],\n [\n [\n 'Clicks',\n fmtInt(t.current.clicks),\n fmtInt(t.previous.clicks),\n fmtChange(t.current.clicks, t.previous.clicks),\n ],\n [\n 'Impressions',\n fmtInt(t.current.impressions),\n fmtInt(t.previous.impressions),\n fmtChange(t.current.impressions, t.previous.impressions),\n ],\n [\n 'CTR',\n fmtPct(t.current.ctr_pct),\n fmtPct(t.previous.ctr_pct),\n `${fmtSigned(t.change.ctr_pp, (v) => v.toFixed(2))} pp`,\n ],\n [\n 'Avg position',\n fmtPos(t.current.position),\n fmtPos(t.previous.position),\n positionChange(t.change.rank_improvement),\n ],\n ],\n );\n const table = (title: string, rows: ComparedRow[]) =>\n rows.length === 0\n ? null\n : sections(\n `**${title}**`,\n mdTable(\n [r.dimension, 'Clicks', 'Δ clicks', 'Impr.', 'Δ impr.', 'Pos.', 'Δ pos.'],\n rows.map((row) => [\n label(row.key),\n fmtInt(row.current.clicks),\n fmtChange(row.current.clicks, row.previous.clicks),\n fmtInt(row.current.impressions),\n fmtSigned(row.change.impressions),\n fmtPos(row.current.position),\n positionChange(row.change.rank_improvement),\n ]),\n ),\n );\n return sections(\n `**${r.site_url}: ${r.current.startDate} → ${r.current.endDate} vs ${r.previous.startDate} → ${r.previous.endDate}** (${r.compare_to === 'year_over_year' ? 'year over year' : 'previous period'}, ${r.data_state} data)`,\n totals,\n table(`Top ${r.dimension} values now`, r.top),\n table('Top gainers (by clicks)', r.gainers),\n table('Top losers (by clicks)', r.losers),\n r.dimension !== 'none'\n ? `_${fmtInt(r.compared_count)} ${r.dimension} values compared: ${fmtInt(r.new_count)} new, ${fmtInt(r.lost_count)} no longer appearing.${r.truncated ? ' Row cap reached; the long tail is not included.' : ''}_`\n : null,\n );\n },\n});\n\nexport function positionChange(improvement: number | null): string {\n if (improvement === null) return '–';\n if (improvement === 0) return '0';\n return improvement > 0 ? `↑${improvement.toFixed(1)}` : `↓${Math.abs(improvement).toFixed(1)}`;\n}\n","import { round, toMetrics, type RawMetrics } from '../format.js';\n\nexport interface DecayRow {\n page: string;\n /** Clicks per window, oldest first. */\n clicks: number[];\n impressions: number[];\n position: (number | null)[];\n baseline_clicks: number;\n latest_clicks: number;\n lost_clicks: number;\n decline_pct: number;\n /** Number of window-to-window declines (sustained decay has many). */\n declines: number;\n likely_cause: 'ranking_loss' | 'lower_demand' | 'lower_ctr' | 'mixed';\n}\n\n/**\n * Pages whose clicks fell steadily across consecutive windows: at least `minBaselineClicks` in the\n * first window, at least `minDeclinePct` lower in the last one, and falling in all but at most one\n * window-to-window step.\n */\nexport function findContentDecay(\n series: Map<string, (RawMetrics | undefined)[]>,\n windows: number,\n opts: { minBaselineClicks: number; minDeclinePct: number },\n): DecayRow[] {\n const out: DecayRow[] = [];\n const requiredDeclines = Math.max(1, windows - 2);\n for (const [page, values] of series) {\n const metrics = Array.from({ length: windows }, (_, i) =>\n toMetrics(values[i] ?? { clicks: 0, impressions: 0 }),\n );\n const clicks = metrics.map((m) => m.clicks);\n const baseline = clicks[0]!;\n const latest = clicks.at(-1)!;\n if (baseline < opts.minBaselineClicks) continue;\n const declinePct = ((baseline - latest) / baseline) * 100;\n if (declinePct < opts.minDeclinePct) continue;\n let declines = 0;\n for (let i = 1; i < clicks.length; i++) if (clicks[i]! < clicks[i - 1]!) declines++;\n if (declines < requiredDeclines) continue;\n\n const first = metrics[0]!;\n const last = metrics.at(-1)!;\n const positionDrop =\n first.position !== null && last.position !== null ? last.position - first.position : 0;\n const impressionDrop = first.impressions\n ? (first.impressions - last.impressions) / first.impressions\n : 0;\n const ctrDrop = first.ctr_pct ? (first.ctr_pct - last.ctr_pct) / first.ctr_pct : 0;\n let cause: DecayRow['likely_cause'] = 'mixed';\n if (positionDrop >= 1.5) cause = 'ranking_loss';\n else if (impressionDrop >= 0.2 && Math.abs(positionDrop) < 1) cause = 'lower_demand';\n else if (ctrDrop >= 0.2 && Math.abs(positionDrop) < 1) cause = 'lower_ctr';\n\n out.push({\n page,\n clicks,\n impressions: metrics.map((m) => m.impressions),\n position: metrics.map((m) => m.position),\n baseline_clicks: baseline,\n latest_clicks: latest,\n lost_clicks: baseline - latest,\n decline_pct: round(declinePct, 1),\n declines,\n likely_cause: cause,\n });\n }\n return out.sort((a, b) => b.lost_clicks - a.lost_clicks);\n}\n","import { z } from 'zod';\nimport { findContentDecay, type DecayRow } from '../analysis/decay.js';\nimport { consecutiveWindows, earliestAvailableDate, todayPT, type DateRange } from '../dates.js';\nimport { UserFacingError } from '../errors.js';\nimport {\n compactUrl,\n fmtInt,\n fmtPos,\n mdTable,\n omittedNote,\n sections,\n type RawMetrics,\n} from '../format.js';\nimport {\n displayHost,\n excludeQueriesField,\n fetchKeyed,\n latestFinalDate,\n limitField,\n propertyFor,\n siteUrlField,\n} from './common.js';\nimport { defineTool, READ_ONLY } from './types.js';\n\nexport interface ContentDecayResult {\n site_url: string;\n windows: DateRange[];\n min_baseline_clicks: number;\n min_decline_pct: number;\n total_matches: number;\n rows: DecayRow[];\n truncated: boolean;\n}\n\nconst CAUSE_LABEL: Record<DecayRow['likely_cause'], string> = {\n ranking_loss: 'rankings dropped',\n lower_demand: 'fewer impressions (demand/seasonality)',\n lower_ctr: 'CTR fell (SERP/snippet change)',\n mixed: 'mixed',\n};\n\nexport const contentDecay = defineTool({\n name: 'content_decay',\n title: 'Content decay',\n description:\n 'Finds pages whose clicks declined steadily over consecutive windows (default 4 × 28 days), not just one bad week. For each page: clicks per window, total decline, position trend and a likely cause (ranking loss, lower demand, lower CTR).',\n input: z.object({\n site_url: siteUrlField,\n windows: z\n .number()\n .int()\n .min(3)\n .max(8)\n .default(4)\n .describe('Number of consecutive windows to compare.'),\n window_days: z\n .number()\n .int()\n .min(7)\n .max(91)\n .default(28)\n .describe('Length of each window in days.'),\n min_baseline_clicks: z\n .number()\n .int()\n .min(1)\n .default(20)\n .describe('Minimum clicks in the oldest window.'),\n min_decline_pct: z\n .number()\n .min(1)\n .max(100)\n .default(25)\n .describe('Minimum drop from the oldest to the newest window, in percent.'),\n exclude_queries: excludeQueriesField,\n limit: limitField(20),\n }),\n annotations: READ_ONLY,\n async run(ctx, input): Promise<ContentDecayResult> {\n const property = await propertyFor(ctx, input.site_url);\n const end = await latestFinalDate(ctx, property.site_url, 'web');\n const windows = consecutiveWindows(end, input.windows, input.window_days);\n const earliest = earliestAvailableDate(todayPT(ctx.now()));\n if (windows[0]!.startDate < earliest) {\n throw new UserFacingError(\n `${input.windows} × ${input.window_days} days reaches back to ${windows[0]!.startDate}, beyond Search Console's ~16 months of data. Use fewer or shorter windows.`,\n );\n }\n const filters = input.exclude_queries\n ? [\n {\n dimension: 'query' as const,\n operator: 'excludingRegex' as const,\n expression: input.exclude_queries,\n },\n ]\n : undefined;\n const results = await Promise.all(\n windows.map((w) => fetchKeyed(ctx, property.site_url, w, 'page', { filters })),\n );\n const series = new Map<string, (RawMetrics | undefined)[]>();\n results.forEach((result, i) => {\n for (const [page, metrics] of result.rows) {\n const url = page.split('#')[0]!;\n const list =\n series.get(url) ?? new Array<RawMetrics | undefined>(windows.length).fill(undefined);\n const prev = list[i];\n list[i] = prev ? mergeMetrics(prev, metrics) : metrics;\n series.set(url, list);\n }\n });\n const matches = findContentDecay(series, windows.length, {\n minBaselineClicks: input.min_baseline_clicks,\n minDeclinePct: input.min_decline_pct,\n });\n return {\n site_url: property.site_url,\n windows,\n min_baseline_clicks: input.min_baseline_clicks,\n min_decline_pct: input.min_decline_pct,\n total_matches: matches.length,\n rows: matches.slice(0, input.limit),\n truncated: results.some((r) => r.truncated),\n };\n },\n format(r) {\n const host = displayHost(r.site_url);\n const span = `${r.windows[0]!.startDate} → ${r.windows.at(-1)!.endDate}, ${r.windows.length} windows`;\n if (r.total_matches === 0) {\n return `No decaying pages on ${r.site_url} (${span}; ≥${r.min_baseline_clicks} clicks in the first window and ≥${r.min_decline_pct}% sustained decline).`;\n }\n return sections(\n `**${fmtInt(r.total_matches)} decaying pages** on ${r.site_url} (${span})`,\n mdTable(\n ['Page', 'Clicks per window', 'Decline', 'Position', 'Likely cause'],\n r.rows.map((row) => [\n compactUrl(row.page, host),\n row.clicks.map(fmtInt).join(' → '),\n `-${fmtInt(row.lost_clicks)} (-${row.decline_pct}%)`,\n `${fmtPos(row.position[0] ?? null)} → ${fmtPos(row.position.at(-1) ?? null)}`,\n CAUSE_LABEL[row.likely_cause],\n ]),\n ),\n omittedNote(r.rows.length, r.total_matches, 'Raise `limit` to see more.'),\n r.truncated ? '_Row cap reached in at least one window; small pages may be missing._' : null,\n );\n },\n});\n\nfunction mergeMetrics(a: RawMetrics, b: RawMetrics): RawMetrics {\n const impressions = a.impressions + b.impressions;\n return {\n clicks: a.clicks + b.clicks,\n impressions,\n position: impressions\n ? ((a.position ?? 0) * a.impressions + (b.position ?? 0) * b.impressions) / impressions\n : undefined,\n };\n}\n","import { z } from 'zod';\nimport { publicErrorMessage, UserFacingError } from '../errors.js';\nimport { compactUrl, mdTable, sections } from '../format.js';\nimport type { GscClient } from '../gsc/client.js';\nimport { listProperties, propertyCoversUrl, resolveProperty, type Property } from '../gsc/sites.js';\nimport type { UrlInspectionResult } from '../gsc/types.js';\nimport { displayHost } from './common.js';\nimport { defineTool, READ_ONLY, type ToolContext } from './types.js';\n\nexport interface InspectionSummary {\n url: string;\n site_url: string;\n verdict: string;\n coverage_state: string | null;\n indexing_state: string | null;\n robots_txt_state: string | null;\n page_fetch_state: string | null;\n last_crawl_time: string | null;\n crawled_as: string | null;\n google_canonical: string | null;\n user_canonical: string | null;\n /** True when Google chose a different canonical than the page declares. */\n canonical_mismatch: boolean;\n sitemaps: string[];\n referring_urls: string[];\n /** Deprecated by Google; reported only when present. */\n mobile_usability: { verdict: string; issues: string[] } | null;\n rich_results: {\n verdict: string;\n items: { type: string; name: string | null; issues: string[] }[];\n } | null;\n inspection_link: string | null;\n}\n\nconst urlField = z\n .string()\n .url()\n .describe('Full URL of the page to inspect (must belong to the property).');\nconst optionalSiteField = z\n .string()\n .optional()\n .describe(\n 'Property that contains the URL. Optional: inferred from your properties when omitted.',\n );\n\n/** Finds the most specific readable property that contains `url`. */\nexport async function propertyForUrl(\n gsc: GscClient,\n url: string,\n siteUrl?: string,\n): Promise<Property> {\n if (siteUrl) {\n const property = await resolveProperty(gsc, siteUrl);\n if (!propertyCoversUrl(property, url)) {\n throw new UserFacingError(`${url} is not inside the property '${property.site_url}'.`);\n }\n return property;\n }\n const properties = (await listProperties(gsc)).filter(\n (p) => p.has_access && propertyCoversUrl(p, url),\n );\n // URL-prefix properties are more specific than domain properties; longer prefixes win.\n properties.sort(\n (a, b) =>\n Number(b.type === 'url_prefix') - Number(a.type === 'url_prefix') ||\n b.site_url.length - a.site_url.length,\n );\n const best = properties[0];\n if (!best)\n throw new UserFacingError(\n `None of your Search Console properties contains ${url}. Run list_sites to check.`,\n 'not_found',\n );\n return best;\n}\n\nexport function summarizeInspection(\n url: string,\n siteUrl: string,\n result: UrlInspectionResult | undefined,\n): InspectionSummary {\n const idx = result?.indexStatusResult ?? {};\n const google = idx.googleCanonical ?? null;\n const user = idx.userCanonical ?? null;\n const issues = (list?: { issueMessage?: string; message?: string; severity?: string }[]) =>\n (list ?? []).map(\n (i) => `${i.severity ? `${i.severity}: ` : ''}${i.issueMessage ?? i.message ?? 'issue'}`,\n );\n return {\n url,\n site_url: siteUrl,\n verdict: idx.verdict ?? 'VERDICT_UNSPECIFIED',\n coverage_state: idx.coverageState ?? null,\n indexing_state: idx.indexingState ?? null,\n robots_txt_state: idx.robotsTxtState ?? null,\n page_fetch_state: idx.pageFetchState ?? null,\n last_crawl_time: idx.lastCrawlTime ?? null,\n crawled_as: idx.crawledAs ?? null,\n google_canonical: google,\n user_canonical: user,\n canonical_mismatch: Boolean(google && user && google !== user),\n sitemaps: idx.sitemap ?? [],\n referring_urls: (idx.referringUrls ?? []).slice(0, 10),\n mobile_usability: result?.mobileUsabilityResult\n ? {\n verdict: result.mobileUsabilityResult.verdict ?? 'VERDICT_UNSPECIFIED',\n issues: issues(result.mobileUsabilityResult.issues),\n }\n : null,\n rich_results: result?.richResultsResult\n ? {\n verdict: result.richResultsResult.verdict ?? 'VERDICT_UNSPECIFIED',\n items: (result.richResultsResult.detectedItems ?? []).flatMap((d) =>\n (d.items ?? [{}]).map((item) => ({\n type: d.richResultType ?? 'unknown',\n name: item.name ?? null,\n issues: issues(item.issues),\n })),\n ),\n }\n : null,\n inspection_link: result?.inspectionResultLink ?? null,\n };\n}\n\nexport async function inspectOne(\n ctx: ToolContext,\n url: string,\n siteUrl?: string,\n languageCode?: string,\n): Promise<InspectionSummary> {\n const property = await propertyForUrl(ctx.gsc, url, siteUrl);\n const res = await ctx.gsc.inspectUrl(property.site_url, url, languageCode);\n return summarizeInspection(url, property.site_url, res.inspectionResult);\n}\n\nexport function formatInspection(s: InspectionSummary): string {\n const lines = [\n `**URL Inspection** for ${s.url}`,\n `- Verdict: **${s.verdict}**${s.coverage_state ? ` — ${s.coverage_state}` : ''}`,\n `- Indexing: ${s.indexing_state ?? 'unknown'} · robots.txt: ${s.robots_txt_state ?? 'unknown'} · fetch: ${s.page_fetch_state ?? 'unknown'}`,\n `- Last crawl: ${s.last_crawl_time ?? 'never / unknown'}${s.crawled_as ? ` (${s.crawled_as.toLowerCase()} crawler)` : ''}`,\n `- Canonical — Google: ${s.google_canonical ?? 'n/a'} · declared: ${s.user_canonical ?? 'n/a'}${s.canonical_mismatch ? ' ⚠️ **mismatch**' : ''}`,\n ];\n if (s.sitemaps.length) lines.push(`- In sitemaps: ${s.sitemaps.join(', ')}`);\n if (s.rich_results) {\n const items = s.rich_results.items\n .map(\n (i) =>\n `${i.type}${i.name ? ` (${i.name})` : ''}${i.issues.length ? `: ${i.issues.join('; ')}` : ''}`,\n )\n .join(' · ');\n lines.push(`- Rich results: ${s.rich_results.verdict}${items ? ` — ${items}` : ''}`);\n }\n if (s.mobile_usability) {\n lines.push(\n `- Mobile usability (deprecated report): ${s.mobile_usability.verdict}${s.mobile_usability.issues.length ? ` — ${s.mobile_usability.issues.join('; ')}` : ''}`,\n );\n }\n if (s.inspection_link) lines.push(`- Open in Search Console: ${s.inspection_link}`);\n lines.push('_URL Inspection reports the indexed version; it is not a live test._');\n return lines.join('\\n');\n}\n\nexport const inspectUrl = defineTool({\n name: 'inspect_url',\n title: 'Inspect URL (index status)',\n description:\n \"Shows Google's index status for one URL: verdict, coverage state, robots.txt and fetch status, last crawl, Google-selected vs declared canonical, sitemaps and rich results. Uses the URL Inspection API (quota: 2,000/day and 600/min per property).\",\n input: z.object({\n url: urlField,\n site_url: optionalSiteField,\n language_code: z.string().default('en-US').describe('Language for issue messages (BCP-47).'),\n }),\n annotations: READ_ONLY,\n async run(ctx, input) {\n return inspectOne(ctx, input.url, input.site_url, input.language_code);\n },\n format: formatInspection,\n});\n\nexport interface BatchInspectionResult {\n inspected: number;\n failed: number;\n verdicts: Record<string, number>;\n coverage: Record<string, number>;\n canonical_mismatches: number;\n results: InspectionSummary[];\n errors: { url: string; error: string }[];\n}\n\nasync function mapWithConcurrency<T, R>(\n items: T[],\n concurrency: number,\n fn: (item: T) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n let next = 0;\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {\n while (next < items.length) {\n const i = next++;\n results[i] = await fn(items[i]!);\n }\n });\n await Promise.all(workers);\n return results;\n}\n\nexport const batchInspectUrls = defineTool({\n name: 'batch_inspect_urls',\n title: 'Inspect many URLs',\n description:\n 'Runs URL Inspection on a list of URLs with limited concurrency and summarizes index status (verdicts, coverage states, canonical mismatches). Each URL costs one URL Inspection call (2,000/day and 600/min per property), so keep batches focused.',\n input: z.object({\n urls: z\n .array(z.string().url())\n .min(1)\n .max(500)\n .describe('URLs to inspect (the server caps the batch size; default cap 50).'),\n site_url: optionalSiteField,\n concurrency: z\n .number()\n .int()\n .min(1)\n .max(10)\n .default(5)\n .describe('Parallel requests (max 10 ≈ the 600/min quota).'),\n }),\n annotations: READ_ONLY,\n async run(ctx, input): Promise<BatchInspectionResult> {\n const urls = [...new Set(input.urls)];\n if (urls.length > ctx.config.batchInspectMax) {\n throw new UserFacingError(\n `Too many URLs (${urls.length}). This server allows up to ${ctx.config.batchInspectMax} per batch (GSCLAW_BATCH_INSPECT_MAX) to protect the 2,000/day URL Inspection quota. Split the list.`,\n );\n }\n const outcomes = await mapWithConcurrency(urls, input.concurrency, async (url) => {\n try {\n return { ok: true as const, value: await inspectOne(ctx, url, input.site_url) };\n } catch (error) {\n return {\n ok: false as const,\n url,\n error: publicErrorMessage(error).message.split('\\n')[0]!,\n };\n }\n });\n const results = outcomes.flatMap((o) => (o.ok ? [o.value] : []));\n const errors = outcomes.flatMap((o) => (o.ok ? [] : [{ url: o.url, error: o.error }]));\n const count = (values: (string | null)[]) =>\n values.reduce<Record<string, number>>((acc, v) => {\n const k = v ?? 'unknown';\n acc[k] = (acc[k] ?? 0) + 1;\n return acc;\n }, {});\n return {\n inspected: results.length,\n failed: errors.length,\n verdicts: count(results.map((r) => r.verdict)),\n coverage: count(results.map((r) => r.coverage_state)),\n canonical_mismatches: results.filter((r) => r.canonical_mismatch).length,\n results,\n errors,\n };\n },\n format(r) {\n const host = r.results[0] ? displayHost(r.results[0].site_url) : undefined;\n const summary = Object.entries(r.coverage)\n .sort((a, b) => b[1] - a[1])\n .map(([state, n]) => `${n} × ${state}`)\n .join(' · ');\n const table = mdTable(\n ['URL', 'Verdict', 'Coverage', 'Last crawl', 'Canonical'],\n r.results.map((s) => [\n compactUrl(s.url, host),\n s.verdict,\n s.coverage_state,\n s.last_crawl_time?.slice(0, 10) ?? '—',\n s.canonical_mismatch\n ? `⚠️ Google chose ${compactUrl(s.google_canonical ?? '', host)}`\n : 'ok',\n ]),\n );\n return sections(\n `**Inspected ${r.inspected} URL(s)**${r.failed ? `, ${r.failed} failed` : ''}. ${summary}`,\n r.canonical_mismatches\n ? `⚠️ ${r.canonical_mismatches} URL(s) where Google picked a different canonical.`\n : null,\n table,\n r.errors.length\n ? `**Errors:**\\n${r.errors.map((e) => `- ${e.url}: ${e.error}`).join('\\n')}`\n : null,\n );\n },\n});\n","import { round } from '../format.js';\n\nexport interface CurvePoint {\n position: number;\n /** Expected CTR in percent. */\n ctr_pct: number;\n source: 'site' | 'industry';\n samples: number;\n}\n\n/**\n * Approximate organic CTR by position (percent), compiled from public click-through-rate studies\n * of Google results. Real CTRs vary a lot by query intent, brand vs non-brand, device and SERP\n * features such as AI Overviews, which is why GSClaw prefers the site's own curve when it has\n * enough data.\n */\nexport const INDUSTRY_CTR_CURVE: readonly number[] = [\n 27.0,\n 15.0,\n 11.0,\n 8.0,\n 6.0,\n 4.6,\n 3.6,\n 2.9,\n 2.4,\n 2.0, // positions 1–10\n 1.6,\n 1.4,\n 1.2,\n 1.1,\n 1.0,\n 0.9,\n 0.8,\n 0.7,\n 0.65,\n 0.6, // positions 11–20\n];\n\nexport const MAX_CURVE_POSITION = INDUSTRY_CTR_CURVE.length;\n\nexport interface CurveInputRow {\n clicks: number;\n impressions: number;\n position: number;\n}\n\nfunction median(values: number[]): number {\n const sorted = [...values].sort((a, b) => a - b);\n const mid = Math.floor(sorted.length / 2);\n return sorted.length % 2 ? sorted[mid]! : (sorted[mid - 1]! + sorted[mid]!) / 2;\n}\n\n/**\n * Builds a CTR-by-position curve. With `benchmark: 'site'`, each position bucket uses the median\n * CTR of the site's own rows (rows with at least `minImpressions`) when it has `minSamples` of\n * them, and the industry curve otherwise. The curve is forced to be non-increasing.\n */\nexport function buildCtrCurve(\n rows: CurveInputRow[],\n opts: { benchmark: 'site' | 'industry'; minImpressions?: number; minSamples?: number },\n): CurvePoint[] {\n const minImpressions = opts.minImpressions ?? 20;\n const minSamples = opts.minSamples ?? 8;\n const buckets = new Map<number, number[]>();\n if (opts.benchmark === 'site') {\n for (const r of rows) {\n if (r.impressions < minImpressions) continue;\n const bucket = Math.round(r.position);\n if (bucket < 1 || bucket > MAX_CURVE_POSITION) continue;\n const list = buckets.get(bucket) ?? [];\n list.push((r.clicks / r.impressions) * 100);\n buckets.set(bucket, list);\n }\n }\n\n const curve: CurvePoint[] = [];\n for (let position = 1; position <= MAX_CURVE_POSITION; position++) {\n const samples = buckets.get(position) ?? [];\n const useSite = samples.length >= minSamples;\n let ctr = useSite ? median(samples) : INDUSTRY_CTR_CURVE[position - 1]!;\n const prev = curve.at(-1);\n if (prev && ctr > prev.ctr_pct) ctr = prev.ctr_pct;\n curve.push({\n position,\n ctr_pct: round(ctr, 2),\n source: useSite ? 'site' : 'industry',\n samples: samples.length,\n });\n }\n return curve;\n}\n\n/** Expected CTR (percent) at a fractional position, interpolating linearly between buckets. */\nexport function expectedCtr(curve: CurvePoint[], position: number): number {\n if (curve.length === 0) return 0;\n if (position <= 1) return curve[0]!.ctr_pct;\n if (position >= curve.length) return curve.at(-1)!.ctr_pct;\n const lower = Math.floor(position);\n const frac = position - lower;\n const a = curve[lower - 1]!.ctr_pct;\n const b = curve[lower]!.ctr_pct;\n return a + (b - a) * frac;\n}\n","import { round, toMetrics, type Metrics } from '../format.js';\nimport { expectedCtr, type CurvePoint } from './ctr-curve.js';\n\n/** A query × page row as returned by Search Console. */\nexport interface QueryPageRow {\n query: string;\n page: string;\n clicks: number;\n impressions: number;\n ctr: number;\n position: number;\n}\n\ninterface PageShare extends Metrics {\n page: string;\n share_pct: number;\n}\n\nfunction groupByQuery(\n rows: QueryPageRow[],\n mergeFragments: boolean,\n): Map<string, Map<string, QueryPageRow>> {\n const byQuery = new Map<string, Map<string, QueryPageRow>>();\n for (const row of rows) {\n const page = mergeFragments ? row.page.split('#')[0]! : row.page;\n const pages = byQuery.get(row.query) ?? new Map<string, QueryPageRow>();\n const existing = pages.get(page);\n if (existing) {\n const impressions = existing.impressions + row.impressions;\n pages.set(page, {\n ...existing,\n clicks: existing.clicks + row.clicks,\n impressions,\n ctr: impressions ? (existing.clicks + row.clicks) / impressions : 0,\n position: impressions\n ? (existing.position * existing.impressions + row.position * row.impressions) /\n impressions\n : existing.position,\n });\n } else {\n pages.set(page, { ...row, page });\n }\n byQuery.set(row.query, pages);\n }\n return byQuery;\n}\n\n// ---------------------------------------------------------------------------------------------\n// Striking distance\n// ---------------------------------------------------------------------------------------------\n\nexport interface StrikingDistanceRow extends Metrics {\n query: string;\n /** The page that ranks for the query (most impressions). */\n page: string;\n /** Other pages of the site that also appear for this query. */\n other_pages: number;\n /** Estimated extra clicks/period if this page reached `target_position`. */\n potential_extra_clicks: number;\n}\n\nexport function findStrikingDistance(\n rows: QueryPageRow[],\n opts: {\n minPosition: number;\n maxPosition: number;\n minImpressions: number;\n targetPosition: number;\n curve: CurvePoint[];\n },\n): StrikingDistanceRow[] {\n const targetCtr = expectedCtr(opts.curve, opts.targetPosition) / 100;\n const out: StrikingDistanceRow[] = [];\n for (const [query, pages] of groupByQuery(rows, true)) {\n const sorted = [...pages.values()].sort((a, b) => b.impressions - a.impressions);\n const primary = sorted[0]!;\n if (primary.position < opts.minPosition || primary.position > opts.maxPosition) continue;\n if (primary.impressions < opts.minImpressions) continue;\n const metrics = toMetrics(primary);\n out.push({\n query,\n page: primary.page,\n ...metrics,\n other_pages: sorted.length - 1,\n potential_extra_clicks: Math.max(\n 0,\n Math.round(primary.impressions * targetCtr - primary.clicks),\n ),\n });\n }\n return out.sort(\n (a, b) => b.potential_extra_clicks - a.potential_extra_clicks || b.impressions - a.impressions,\n );\n}\n\n// ---------------------------------------------------------------------------------------------\n// CTR opportunities\n// ---------------------------------------------------------------------------------------------\n\nexport interface CtrOpportunityRow extends Metrics {\n key: string;\n expected_ctr_pct: number;\n /** Actual minus expected CTR, in percentage points (negative = underperforming). */\n ctr_gap_pp: number;\n /** Clicks lost versus the expected CTR at this position. */\n missed_clicks: number;\n}\n\nexport function findCtrOpportunities(\n rows: { key: string; clicks: number; impressions: number; ctr: number; position: number }[],\n opts: { maxPosition: number; minImpressions: number; threshold: number; curve: CurvePoint[] },\n): CtrOpportunityRow[] {\n const out: CtrOpportunityRow[] = [];\n for (const row of rows) {\n if (row.impressions < opts.minImpressions || row.position > opts.maxPosition) continue;\n const expected = expectedCtr(opts.curve, row.position);\n const actual = (row.clicks / row.impressions) * 100;\n if (actual >= expected * opts.threshold) continue;\n out.push({\n key: row.key,\n ...toMetrics(row),\n expected_ctr_pct: round(expected, 2),\n ctr_gap_pp: round(actual - expected, 2),\n missed_clicks: Math.round(row.impressions * (expected / 100) - row.clicks),\n });\n }\n return out.sort((a, b) => b.missed_clicks - a.missed_clicks);\n}\n\n// ---------------------------------------------------------------------------------------------\n// Keyword cannibalization\n// ---------------------------------------------------------------------------------------------\n\nexport interface CannibalizationRow {\n query: string;\n clicks: number;\n impressions: number;\n /** Impressions that went to pages other than the top one. */\n contested_impressions: number;\n competing_pages: number;\n pages: PageShare[];\n}\n\nexport function findCannibalization(\n rows: QueryPageRow[],\n opts: { minImpressions: number; minSharePct: number; maxPagesShown?: number },\n): CannibalizationRow[] {\n const out: CannibalizationRow[] = [];\n for (const [query, pages] of groupByQuery(rows, true)) {\n if (pages.size < 2) continue;\n const list = [...pages.values()];\n const impressions = list.reduce((s, r) => s + r.impressions, 0);\n if (impressions < opts.minImpressions) continue;\n const shares: PageShare[] = list\n .map((r) => ({\n page: r.page,\n ...toMetrics(r),\n share_pct: round((r.impressions / impressions) * 100, 1),\n }))\n .sort((a, b) => b.impressions - a.impressions);\n const competing = shares.filter((s) => s.share_pct >= opts.minSharePct);\n if (competing.length < 2) continue;\n out.push({\n query,\n clicks: list.reduce((s, r) => s + r.clicks, 0),\n impressions,\n contested_impressions: impressions - shares[0]!.impressions,\n competing_pages: competing.length,\n pages: shares.slice(0, opts.maxPagesShown ?? 5),\n });\n }\n return out.sort((a, b) => b.contested_impressions - a.contested_impressions);\n}\n","import { z } from 'zod';\nimport { buildCtrCurve, type CurvePoint } from '../analysis/ctr-curve.js';\nimport {\n findCannibalization,\n findCtrOpportunities,\n findStrikingDistance,\n type CannibalizationRow,\n type CtrOpportunityRow,\n type QueryPageRow,\n type StrikingDistanceRow,\n} from '../analysis/opportunities.js';\nimport { compactUrl, fmtInt, fmtPct, fmtPos, mdTable, omittedNote, sections } from '../format.js';\nimport type { SearchAnalyticsRow } from '../gsc/types.js';\nimport {\n dateRangeFields,\n displayHost,\n excludeQueriesField,\n fetchRows,\n limitField,\n propertyFor,\n resolveWindow,\n siteUrlField,\n windowNote,\n withQueryExclusion,\n type ResolvedWindow,\n} from './common.js';\nimport { defineTool, READ_ONLY, type ToolContext } from './types.js';\n\nfunction toQueryPageRows(rows: SearchAnalyticsRow[]): QueryPageRow[] {\n return rows.map((r) => ({\n query: r.keys?.[0] ?? '',\n page: r.keys?.[1] ?? '',\n clicks: r.clicks,\n impressions: r.impressions,\n ctr: r.ctr,\n position: r.position,\n }));\n}\n\nasync function queryPageRows(\n ctx: ToolContext,\n siteUrl: string,\n window: ResolvedWindow,\n excludeQueries?: string,\n) {\n const { rows, truncated } = await fetchRows(ctx, siteUrl, window, ['query', 'page'], {\n filters: withQueryExclusion(undefined, excludeQueries),\n dataState: window.dataState,\n });\n return { rows: toQueryPageRows(rows), truncated };\n}\n\nconst truncatedNote = (truncated: boolean) =>\n truncated\n ? '_Row cap reached: only the top rows by clicks were analysed (raise GSCLAW_MAX_ROWS or shorten the date range)._'\n : null;\n\n// ---------------------------------------------------------------------------------------------\n\nexport interface StrikingDistanceResult {\n site_url: string;\n start_date: string;\n end_date: string;\n data_state: string;\n position_range: [number, number];\n min_impressions: number;\n target_position: number;\n expected_ctr_at_target_pct: number;\n total_matches: number;\n rows: StrikingDistanceRow[];\n truncated: boolean;\n}\n\nexport const strikingDistanceKeywords = defineTool({\n name: 'striking_distance_keywords',\n title: 'Striking-distance keywords',\n description:\n 'Finds queries ranking just off the top spots (default average position 8–20) with meaningful impressions: the cheapest wins in SEO. ' +\n 'Returns the ranking page, position, impressions and an estimate of extra clicks if the page reached position 3 (based on the site’s own CTR curve).',\n input: z.object({\n site_url: siteUrlField,\n ...dateRangeFields,\n min_position: z.number().min(1).max(100).default(8),\n max_position: z.number().min(1).max(100).default(20),\n min_impressions: z\n .number()\n .int()\n .min(0)\n .default(20)\n .describe('Minimum impressions in the period.'),\n target_position: z\n .number()\n .min(1)\n .max(10)\n .default(3)\n .describe('Position used to estimate potential clicks.'),\n exclude_queries: excludeQueriesField,\n limit: limitField(25),\n }),\n annotations: READ_ONLY,\n async run(ctx, input): Promise<StrikingDistanceResult> {\n const property = await propertyFor(ctx, input.site_url);\n const window = await resolveWindow(ctx, property, input);\n const { rows, truncated } = await queryPageRows(\n ctx,\n property.site_url,\n window,\n input.exclude_queries,\n );\n const curve = buildCtrCurve(rows, { benchmark: 'site' });\n const matches = findStrikingDistance(rows, {\n minPosition: input.min_position,\n maxPosition: input.max_position,\n minImpressions: input.min_impressions,\n targetPosition: input.target_position,\n curve,\n });\n return {\n site_url: property.site_url,\n start_date: window.startDate,\n end_date: window.endDate,\n data_state: window.dataState,\n position_range: [input.min_position, input.max_position],\n min_impressions: input.min_impressions,\n target_position: input.target_position,\n expected_ctr_at_target_pct: curve[Math.round(input.target_position) - 1]?.ctr_pct ?? 0,\n total_matches: matches.length,\n rows: matches.slice(0, input.limit),\n truncated,\n };\n },\n format(r) {\n const host = displayHost(r.site_url);\n if (r.total_matches === 0) {\n return `No striking-distance queries for ${r.site_url} (position ${r.position_range[0]}–${r.position_range[1]}, ≥${r.min_impressions} impressions, ${r.start_date} → ${r.end_date}). Try a longer date range or lower min_impressions.`;\n }\n return sections(\n `**${fmtInt(r.total_matches)} striking-distance queries** for ${r.site_url} (avg position ${r.position_range[0]}–${r.position_range[1]}, ≥${r.min_impressions} impressions)`,\n windowNote({\n startDate: r.start_date,\n endDate: r.end_date,\n dataState: r.data_state === 'all' ? 'all' : 'final',\n label: '',\n }),\n mdTable(\n [\n 'Query',\n 'Ranking page',\n 'Pos.',\n 'Impr.',\n 'Clicks',\n 'CTR',\n `+Clicks @#${r.target_position}`,\n ],\n r.rows.map((row) => [\n row.query,\n compactUrl(row.page, host) + (row.other_pages ? ` (+${row.other_pages} other)` : ''),\n fmtPos(row.position),\n fmtInt(row.impressions),\n fmtInt(row.clicks),\n fmtPct(row.ctr_pct),\n `+${fmtInt(row.potential_extra_clicks)}`,\n ]),\n ),\n omittedNote(r.rows.length, r.total_matches, 'Raise `limit` to see more.'),\n `_Potential clicks assume ${fmtPct(r.expected_ctr_at_target_pct)} CTR at position ${r.target_position} (your site's CTR curve where data allows, otherwise an industry average). \"+N other\" = more pages competing for the query; see keyword_cannibalization._`,\n truncatedNote(r.truncated),\n );\n },\n});\n\n// ---------------------------------------------------------------------------------------------\n\nexport interface CtrOpportunitiesResult {\n site_url: string;\n start_date: string;\n end_date: string;\n data_state: string;\n dimension: 'query' | 'page';\n benchmark: 'site' | 'industry';\n threshold_pct: number;\n min_impressions: number;\n max_position: number;\n curve: CurvePoint[];\n total_matches: number;\n rows: CtrOpportunityRow[];\n truncated: boolean;\n}\n\nexport const ctrOpportunities = defineTool({\n name: 'ctr_opportunities',\n title: 'CTR opportunities',\n description:\n 'Finds queries or pages with many impressions but a CTR well below what their position usually earns: candidates for better titles, meta descriptions and rich results. ' +\n 'Compares against a CTR-by-position curve (the site’s own by default, falling back to an industry curve) and returns the curve used plus estimated missed clicks.',\n input: z.object({\n site_url: siteUrlField,\n ...dateRangeFields,\n dimension: z.enum(['query', 'page']).default('query'),\n max_position: z\n .number()\n .min(1)\n .max(20)\n .default(10)\n .describe('Only consider rows ranking at or above this average position.'),\n min_impressions: z.number().int().min(0).default(100),\n benchmark: z\n .enum(['site', 'industry'])\n .default('site')\n .describe(\n 'site = your own median CTR per position (industry values fill gaps); industry = a generic curve from public CTR studies.',\n ),\n threshold_pct: z\n .number()\n .min(1)\n .max(100)\n .default(60)\n .describe('Flag rows whose CTR is below this % of the expected CTR (default 60%).'),\n exclude_queries: excludeQueriesField,\n limit: limitField(25),\n }),\n annotations: READ_ONLY,\n async run(ctx, input): Promise<CtrOpportunitiesResult> {\n const property = await propertyFor(ctx, input.site_url);\n const window = await resolveWindow(ctx, property, input);\n const { rows, truncated } = await fetchRows(ctx, property.site_url, window, [input.dimension], {\n filters: withQueryExclusion(undefined, input.exclude_queries),\n dataState: window.dataState,\n });\n const keyed = rows.map((r) => ({ key: r.keys?.[0] ?? '', ...r }));\n const curve = buildCtrCurve(keyed, { benchmark: input.benchmark });\n const matches = findCtrOpportunities(keyed, {\n maxPosition: input.max_position,\n minImpressions: input.min_impressions,\n threshold: input.threshold_pct / 100,\n curve,\n });\n return {\n site_url: property.site_url,\n start_date: window.startDate,\n end_date: window.endDate,\n data_state: window.dataState,\n dimension: input.dimension,\n benchmark: input.benchmark,\n threshold_pct: input.threshold_pct,\n min_impressions: input.min_impressions,\n max_position: input.max_position,\n curve: curve.slice(0, Math.max(10, Math.ceil(input.max_position))),\n total_matches: matches.length,\n rows: matches.slice(0, input.limit),\n truncated,\n };\n },\n format(r) {\n const host = displayHost(r.site_url);\n const curve = r.curve\n .map((p) => `#${p.position}: ${fmtPct(p.ctr_pct)}${p.source === 'industry' ? '*' : ''}`)\n .join(' · ');\n if (r.total_matches === 0) {\n return sections(\n `No CTR opportunities for ${r.site_url}: no ${r.dimension} with ≥${r.min_impressions} impressions at position ≤${r.max_position} has a CTR below ${r.threshold_pct}% of the expected value.`,\n `_Benchmark curve: ${curve}_`,\n );\n }\n return sections(\n `**${fmtInt(r.total_matches)} ${r.dimension === 'query' ? 'queries' : 'pages'} with below-expected CTR** for ${r.site_url} (CTR < ${r.threshold_pct}% of the ${r.benchmark} benchmark for their position)`,\n windowNote({\n startDate: r.start_date,\n endDate: r.end_date,\n dataState: r.data_state === 'all' ? 'all' : 'final',\n label: '',\n }),\n mdTable(\n [\n r.dimension === 'page' ? 'Page' : 'Query',\n 'Pos.',\n 'Impr.',\n 'Clicks',\n 'CTR',\n 'Expected',\n 'Missed clicks',\n ],\n r.rows.map((row) => [\n r.dimension === 'page' ? compactUrl(row.key, host) : row.key,\n fmtPos(row.position),\n fmtInt(row.impressions),\n fmtInt(row.clicks),\n fmtPct(row.ctr_pct),\n fmtPct(row.expected_ctr_pct),\n fmtInt(row.missed_clicks),\n ]),\n ),\n omittedNote(r.rows.length, r.total_matches, 'Raise `limit` to see more.'),\n `_Benchmark CTR by position (${r.benchmark}; * = industry fallback): ${curve}. Branded queries inflate CTR at top positions; use exclude_queries for a non-brand view._`,\n truncatedNote(r.truncated),\n );\n },\n});\n\n// ---------------------------------------------------------------------------------------------\n\nexport interface CannibalizationResult {\n site_url: string;\n start_date: string;\n end_date: string;\n data_state: string;\n min_impressions: number;\n min_share_pct: number;\n total_matches: number;\n rows: CannibalizationRow[];\n truncated: boolean;\n}\n\nexport const keywordCannibalization = defineTool({\n name: 'keyword_cannibalization',\n title: 'Keyword cannibalization',\n description:\n \"Finds queries where two or more of the site's pages each get a meaningful share of impressions, so they compete with each other. Returns each page's impression share, clicks and position. URL #fragments are merged into their page.\",\n input: z.object({\n site_url: siteUrlField,\n ...dateRangeFields,\n min_impressions: z\n .number()\n .int()\n .min(0)\n .default(50)\n .describe('Minimum total impressions for the query.'),\n min_share_pct: z\n .number()\n .min(1)\n .max(50)\n .default(10)\n .describe(\n 'A page counts as competing if it has at least this share of the query’s impressions.',\n ),\n exclude_queries: excludeQueriesField,\n limit: limitField(20),\n }),\n annotations: READ_ONLY,\n async run(ctx, input): Promise<CannibalizationResult> {\n const property = await propertyFor(ctx, input.site_url);\n const window = await resolveWindow(ctx, property, input);\n const { rows, truncated } = await queryPageRows(\n ctx,\n property.site_url,\n window,\n input.exclude_queries,\n );\n const matches = findCannibalization(rows, {\n minImpressions: input.min_impressions,\n minSharePct: input.min_share_pct,\n });\n return {\n site_url: property.site_url,\n start_date: window.startDate,\n end_date: window.endDate,\n data_state: window.dataState,\n min_impressions: input.min_impressions,\n min_share_pct: input.min_share_pct,\n total_matches: matches.length,\n rows: matches.slice(0, input.limit),\n truncated,\n };\n },\n format(r) {\n const host = displayHost(r.site_url);\n if (r.total_matches === 0) {\n return `No cannibalization found for ${r.site_url}: no query with ≥${r.min_impressions} impressions has 2+ pages each above ${r.min_share_pct}% share (${r.start_date} → ${r.end_date}).`;\n }\n const blocks = r.rows.map((q) =>\n sections(\n `**\"${q.query}\"** — ${fmtInt(q.impressions)} impr., ${fmtInt(q.clicks)} clicks, ${q.competing_pages} competing pages`,\n mdTable(\n ['Page', 'Share', 'Impr.', 'Clicks', 'Pos.'],\n q.pages.map((p) => [\n compactUrl(p.page, host),\n fmtPct(p.share_pct),\n fmtInt(p.impressions),\n fmtInt(p.clicks),\n fmtPos(p.position),\n ]),\n ),\n ),\n );\n return sections(\n `**${fmtInt(r.total_matches)} cannibalized queries** for ${r.site_url} (sorted by impressions not going to the top page)`,\n windowNote({\n startDate: r.start_date,\n endDate: r.end_date,\n dataState: r.data_state === 'all' ? 'all' : 'final',\n label: '',\n }),\n ...blocks,\n omittedNote(r.rows.length, r.total_matches, 'Raise `limit` to see more.'),\n truncatedNote(r.truncated),\n );\n },\n});\n","import { z } from 'zod';\nimport {\n compareRows,\n metricChange,\n topByCurrentClicks,\n type ComparedRow,\n type MetricChange,\n} from '../analysis/compare.js';\nimport { COMPARE_MODES, comparisonRange, type CompareMode, type DateRange } from '../dates.js';\nimport { publicErrorMessage, UserFacingError } from '../errors.js';\nimport {\n fmtChange,\n fmtInt,\n fmtPct,\n fmtPos,\n mdTable,\n sections,\n toMetrics,\n type Metrics,\n} from '../format.js';\nimport { absolutePageUrl, type Property } from '../gsc/sites.js';\nimport type { DimensionFilter } from '../gsc/types.js';\nimport { positionChange } from './compare.js';\nimport {\n dateRangeFields,\n fetchKeyed,\n fetchRows,\n fetchTotals,\n limitField,\n propertyFor,\n resolveWindow,\n type ResolvedWindow,\n} from './common.js';\nimport {\n formatInspection,\n inspectOne,\n propertyForUrl,\n type InspectionSummary,\n} from './inspection.js';\nimport { defineTool, READ_ONLY, type ToolContext } from './types.js';\n\nexport interface PageReportResult {\n site_url: string;\n page: string;\n current: DateRange;\n previous: DateRange;\n compare_to: CompareMode;\n data_state: 'final' | 'all';\n totals: { current: Metrics; previous: Metrics; change: MetricChange };\n trend: ({ date: string } & Metrics)[];\n top_queries: ComparedRow[];\n query_count: number;\n inspection: InspectionSummary | null;\n inspection_error: string | null;\n}\n\n/** Turns a path like '/pricing' into the page URL Search Console reports, using the page's own data. */\nasync function resolvePageUrl(\n ctx: ToolContext,\n property: Property,\n page: string,\n window: ResolvedWindow,\n): Promise<string> {\n const direct = absolutePageUrl(property, page);\n if (direct) return direct;\n if (!page.startsWith('/'))\n throw new UserFacingError(`'${page}' is not a URL or a path starting with '/'.`);\n const { rows } = await fetchRows(ctx, property.site_url, window, ['page'], {\n filters: [{ dimension: 'page', operator: 'contains', expression: page }],\n maxRows: 1000,\n });\n const candidates = rows\n .map((r) => ({ url: r.keys?.[0] ?? '', impressions: r.impressions }))\n .filter((c) => {\n try {\n return new URL(c.url).pathname === page;\n } catch {\n return false;\n }\n })\n .sort((a, b) => b.impressions - a.impressions);\n if (!candidates[0]) {\n throw new UserFacingError(\n `No page with path '${page}' has Search data on ${property.site_url} in this period. Pass the full URL (e.g. https://example.com${page}).`,\n 'not_found',\n );\n }\n return candidates[0].url;\n}\n\nexport const pageReport = defineTool({\n name: 'page_report',\n title: 'Page report',\n description:\n \"One-call report for a single page: clicks/impressions/CTR/position vs the previous period, a daily trend, the page's top queries with deltas, and its URL Inspection status (index state, canonical, last crawl).\",\n input: z.object({\n page: z\n .string()\n .min(1)\n .describe(\"Full page URL, or a path like '/pricing' (then site_url is required).\"),\n site_url: z.string().optional().describe('Property. Optional when page is a full URL.'),\n ...dateRangeFields,\n compare_to: z.enum(COMPARE_MODES).default('previous_period'),\n top_queries: limitField(15, 100),\n include_inspection: z\n .boolean()\n .default(true)\n .describe('Also run URL Inspection (uses 1 of 2,000 daily inspections).'),\n }),\n annotations: READ_ONLY,\n async run(ctx, input): Promise<PageReportResult> {\n let property: Property;\n if (/^https?:\\/\\//i.test(input.page))\n property = await propertyForUrl(ctx.gsc, input.page, input.site_url);\n else if (input.site_url) property = await propertyFor(ctx, input.site_url);\n else throw new UserFacingError('Pass site_url when page is a path.');\n\n const current = await resolveWindow(ctx, property, input);\n const previous = comparisonRange(current, input.compare_to);\n const pageUrl = await resolvePageUrl(ctx, property, input.page, current);\n const filters: DimensionFilter[] = [\n { dimension: 'page', operator: 'equals', expression: pageUrl },\n ];\n const opts = { filters, dataState: current.dataState };\n\n const inspection = input.include_inspection\n ? inspectOne(ctx, pageUrl, property.site_url).then(\n (value) => ({ value, error: null }),\n (error: unknown) => ({\n value: null,\n error: publicErrorMessage(error).message.split('\\n')[0]!,\n }),\n )\n : Promise.resolve({ value: null, error: null });\n\n const [curTotals, prevTotals, trendRows, curQueries, prevQueries, inspected] =\n await Promise.all([\n fetchTotals(ctx, property.site_url, current, opts),\n fetchTotals(ctx, property.site_url, previous, opts),\n fetchRows(ctx, property.site_url, current, ['date'], opts),\n fetchKeyed(ctx, property.site_url, current, 'query', { ...opts, maxRows: 5000 }),\n fetchKeyed(ctx, property.site_url, previous, 'query', { ...opts, maxRows: 5000 }),\n inspection,\n ]);\n\n const cur = toMetrics(curTotals);\n const prev = toMetrics(prevTotals);\n const queries = compareRows(curQueries.rows, prevQueries.rows);\n return {\n site_url: property.site_url,\n page: pageUrl,\n current: { startDate: current.startDate, endDate: current.endDate },\n previous,\n compare_to: input.compare_to,\n data_state: current.dataState,\n totals: { current: cur, previous: prev, change: metricChange(cur, prev) },\n trend: trendRows.rows\n .map((r) => ({ date: r.keys?.[0] ?? '', ...toMetrics(r) }))\n .sort((a, b) => a.date.localeCompare(b.date)),\n top_queries: topByCurrentClicks(queries, input.top_queries),\n query_count: curQueries.rows.size,\n inspection: inspected.value,\n inspection_error: inspected.error,\n };\n },\n format(r) {\n const t = r.totals;\n const weekly = summarizeTrend(r.trend);\n return sections(\n `**Page report:** ${r.page}`,\n `_${r.current.startDate} → ${r.current.endDate} vs ${r.previous.startDate} → ${r.previous.endDate} (${r.compare_to === 'year_over_year' ? 'year over year' : 'previous period'}, ${r.data_state} data)._`,\n mdTable(\n ['Metric', 'Current', 'Previous', 'Change'],\n [\n [\n 'Clicks',\n fmtInt(t.current.clicks),\n fmtInt(t.previous.clicks),\n fmtChange(t.current.clicks, t.previous.clicks),\n ],\n [\n 'Impressions',\n fmtInt(t.current.impressions),\n fmtInt(t.previous.impressions),\n fmtChange(t.current.impressions, t.previous.impressions),\n ],\n [\n 'CTR',\n fmtPct(t.current.ctr_pct),\n fmtPct(t.previous.ctr_pct),\n `${t.change.ctr_pp >= 0 ? '+' : ''}${t.change.ctr_pp.toFixed(2)} pp`,\n ],\n [\n 'Avg position',\n fmtPos(t.current.position),\n fmtPos(t.previous.position),\n positionChange(t.change.rank_improvement),\n ],\n ],\n ),\n weekly ? `**Trend (clicks per 7 days, oldest → newest):** ${weekly}` : null,\n r.top_queries.length\n ? sections(\n `**Top queries** (${fmtInt(r.query_count)} total)`,\n mdTable(\n ['Query', 'Clicks', 'Δ clicks', 'Impr.', 'CTR', 'Pos.', 'Δ pos.'],\n r.top_queries.map((q) => [\n q.key,\n fmtInt(q.current.clicks),\n fmtChange(q.current.clicks, q.previous.clicks),\n fmtInt(q.current.impressions),\n fmtPct(q.current.ctr_pct),\n fmtPos(q.current.position),\n positionChange(q.change.rank_improvement),\n ]),\n ),\n )\n : '_No query data for this page in the period._',\n r.inspection\n ? formatInspection(r.inspection)\n : r.inspection_error\n ? `_URL Inspection failed: ${r.inspection_error}_`\n : null,\n );\n },\n});\n\nfunction summarizeTrend(trend: ({ date: string } & Metrics)[]): string | null {\n if (trend.length < 7) return trend.length ? trend.map((d) => fmtInt(d.clicks)).join(' → ') : null;\n const buckets: number[] = [];\n // Bucket from the newest day backwards so the last bucket is a full week.\n for (let end = trend.length; end > 0; end -= 7) {\n const slice = trend.slice(Math.max(0, end - 7), end);\n if (slice.length === 7) buckets.unshift(slice.reduce((s, d) => s + d.clicks, 0));\n }\n return buckets.map(fmtInt).join(' → ');\n}\n","import { z } from 'zod';\nimport { UserFacingError } from '../errors.js';\nimport {\n compactUrl,\n fmtInt,\n fmtPct,\n fmtPos,\n mdTable,\n omittedNote,\n sections,\n toMetrics,\n type Metrics,\n} from '../format.js';\nimport {\n AGGREGATION_TYPES,\n DIMENSIONS,\n type Dimension,\n type SearchAnalyticsRequest,\n} from '../gsc/types.js';\nimport {\n dateRangeFields,\n displayHost,\n filtersField,\n limitField,\n propertyFor,\n resolveWindow,\n searchTypeField,\n siteUrlField,\n toFilterGroups,\n windowNote,\n} from './common.js';\nimport { defineTool, READ_ONLY } from './types.js';\n\nconst SORT_KEYS = ['clicks', 'impressions', 'ctr', 'position'] as const;\n\nexport interface AnalyticsRow extends Metrics {\n keys: Partial<Record<Dimension, string>>;\n}\n\nexport interface SearchAnalyticsResult {\n site_url: string;\n start_date: string;\n end_date: string;\n data_state: string;\n search_type: string;\n dimensions: Dimension[];\n totals: Metrics | null;\n rows: AnalyticsRow[];\n total_rows: number;\n /** True when the row cap was reached and more rows may exist. */\n truncated: boolean;\n first_incomplete_date?: string;\n}\n\nexport const searchAnalytics = defineTool({\n name: 'search_analytics',\n title: 'Search performance (raw query)',\n description:\n 'Queries Search Console performance data (clicks, impressions, CTR, average position) grouped by any dimensions, with filters (equals, contains, regex…), search type and aggregation type. Auto-paginates up to row_limit. ' +\n 'Use for custom breakdowns; prefer the analysis tools (compare_periods, striking_distance_keywords, …) for common questions. ' +\n 'Note: the API exposes only top rows (max ~50K/day/search type), and anonymized rare queries are omitted from query rows but included in totals.',\n input: z.object({\n site_url: siteUrlField,\n ...dateRangeFields,\n dimensions: z\n .array(z.enum(DIMENSIONS))\n .max(5)\n .default(['query'])\n .describe(\n 'Group by. searchAppearance cannot be combined with other dimensions. hour requires recent dates (last ~10 days) and switches to hourly data. Use [] for property totals.',\n ),\n filters: filtersField,\n search_type: searchTypeField,\n aggregation_type: z\n .enum(AGGREGATION_TYPES)\n .default('auto')\n .describe(\n 'auto (default), byPage, byProperty (not with page dimension/filter), byNewsShowcasePanel.',\n ),\n row_limit: z\n .number()\n .int()\n .min(1)\n .max(500_000)\n .default(1000)\n .describe(\n 'Rows to fetch from Google (auto-paginated in 25K pages; capped by the server’s GSCLAW_MAX_ROWS).',\n ),\n order_by: z\n .enum(SORT_KEYS)\n .default('clicks')\n .describe('Sort shown rows by this metric (position sorts ascending).'),\n limit: limitField(25),\n include_totals: z\n .boolean()\n .default(true)\n .describe('Also fetch property totals for the same filters (one extra request).'),\n }),\n annotations: READ_ONLY,\n async run(ctx, input): Promise<SearchAnalyticsResult> {\n if (input.dimensions.includes('searchAppearance') && input.dimensions.length > 1) {\n throw new UserFacingError(\n 'searchAppearance cannot be combined with other dimensions. Query searchAppearance alone first, then filter on one appearance and group by other dimensions.',\n );\n }\n const property = await propertyFor(ctx, input.site_url);\n const window = await resolveWindow(ctx, property, input, input.search_type);\n const hourly = input.dimensions.includes('hour');\n const base: Omit<SearchAnalyticsRequest, 'rowLimit' | 'startRow'> = {\n startDate: window.startDate,\n endDate: window.endDate,\n type: input.search_type,\n dimensionFilterGroups: toFilterGroups(input.filters),\n aggregationType: input.aggregation_type,\n dataState: hourly ? 'hourly_all' : window.dataState,\n };\n\n const maxRows = Math.min(input.row_limit, ctx.config.maxRows);\n const [result, totalsRes] = await Promise.all([\n input.dimensions.length > 0\n ? ctx.gsc.searchAnalyticsAll(\n property.site_url,\n { ...base, dimensions: input.dimensions },\n maxRows,\n )\n : Promise.resolve(null),\n input.include_totals || input.dimensions.length === 0\n ? ctx.gsc.searchAnalytics(property.site_url, { ...base, rowLimit: 1 })\n : Promise.resolve(null),\n ]);\n\n const rows: AnalyticsRow[] = (result?.rows ?? []).map((r) => ({\n keys: Object.fromEntries(input.dimensions.map((d, i) => [d, r.keys?.[i] ?? ''])),\n ...toMetrics(r),\n }));\n sortRows(rows, input.order_by);\n\n const totalRow = totalsRes?.rows?.[0];\n const meta = result?.metadata ?? totalsRes?.metadata;\n return {\n site_url: property.site_url,\n start_date: window.startDate,\n end_date: window.endDate,\n data_state: base.dataState ?? 'final',\n search_type: input.search_type,\n dimensions: input.dimensions,\n totals: totalRow\n ? toMetrics(totalRow)\n : totalsRes\n ? toMetrics({ clicks: 0, impressions: 0 })\n : null,\n rows: rows.slice(0, input.limit),\n total_rows: rows.length,\n truncated: result?.truncated ?? false,\n first_incomplete_date: meta?.first_incomplete_date ?? meta?.firstIncompleteDate,\n };\n },\n format(r) {\n const host = displayHost(r.site_url);\n const totals = r.totals\n ? `**Totals:** ${fmtInt(r.totals.clicks)} clicks · ${fmtInt(r.totals.impressions)} impressions · CTR ${fmtPct(r.totals.ctr_pct)} · avg position ${fmtPos(r.totals.position)}`\n : null;\n const table =\n r.dimensions.length > 0\n ? mdTable(\n [...r.dimensions, 'Clicks', 'Impr.', 'CTR', 'Pos.'],\n r.rows.map((row) => [\n ...r.dimensions.map((d) =>\n d === 'page' ? compactUrl(row.keys[d] ?? '', host) : row.keys[d],\n ),\n fmtInt(row.clicks),\n fmtInt(row.impressions),\n fmtPct(row.ctr_pct),\n fmtPos(row.position),\n ]),\n )\n : null;\n return sections(\n `**Search analytics** for ${r.site_url} (${r.search_type}${r.dimensions.length ? `, by ${r.dimensions.join(' × ')}` : ''})`,\n windowNote({\n startDate: r.start_date,\n endDate: r.end_date,\n dataState: r.data_state === 'final' ? 'final' : 'all',\n label: '',\n }),\n totals,\n table,\n omittedNote(r.rows.length, r.total_rows, 'Raise `limit` to see more, or add filters.'),\n r.truncated\n ? `_Row cap reached (${fmtInt(r.total_rows)} rows fetched); more rows may exist. Raise row_limit or narrow filters/dates._`\n : null,\n r.first_incomplete_date\n ? `_Data from ${r.first_incomplete_date} onward is incomplete._`\n : null,\n );\n },\n});\n\nfunction sortRows(rows: AnalyticsRow[], key: (typeof SORT_KEYS)[number]): void {\n const value = (r: AnalyticsRow) =>\n key === 'ctr' ? r.ctr_pct : key === 'position' ? -(r.position ?? Infinity) : r[key];\n rows.sort((a, b) => value(b) - value(a));\n}\n","import { z } from 'zod';\nimport { listProperties, type Property } from '../gsc/sites.js';\nimport { mdTable, sections } from '../format.js';\nimport { defineTool, READ_ONLY } from './types.js';\n\nexport const listSites = defineTool({\n name: 'list_sites',\n title: 'List Search Console properties',\n description:\n \"Lists every Search Console property this server can access, with its type (domain 'sc-domain:' vs URL-prefix) and permission level. Call this first to find the exact property to pass as site_url.\",\n input: z.object({}),\n annotations: READ_ONLY,\n async run(ctx): Promise<{ identity: string; properties: Property[] }> {\n return { identity: ctx.gsc.identity.email, properties: await listProperties(ctx.gsc) };\n },\n format(result) {\n if (result.properties.length === 0) {\n return `No Search Console properties are visible to ${result.identity}. Add it as a user on your properties: Search Console → Settings → Users and permissions → Add user.`;\n }\n const table = mdTable(\n ['Property', 'Type', 'Permission'],\n result.properties.map((p) => [\n p.site_url,\n p.type === 'domain' ? 'domain' : 'URL prefix',\n p.has_access ? p.permission_level : 'unverified (no data)',\n ]),\n );\n return sections(\n `**${result.properties.length} properties** visible to ${result.identity}:`,\n table,\n );\n },\n});\n","import { z } from 'zod';\nimport { UserFacingError } from '../errors.js';\nimport { fmtInt, mdTable, sections } from '../format.js';\nimport type { Sitemap } from '../gsc/types.js';\nimport { propertyFor, siteUrlField } from './common.js';\nimport { defineTool, READ_ONLY } from './types.js';\n\nexport interface SitemapSummary {\n path: string;\n type: string | null;\n is_index: boolean;\n is_pending: boolean;\n last_submitted: string | null;\n last_downloaded: string | null;\n errors: number;\n warnings: number;\n /** URLs submitted per content type. Google no longer reports indexed counts for sitemaps. */\n submitted: number;\n contents: { type: string; submitted: number }[];\n}\n\nconst num = (v: string | number | undefined) => (v === undefined ? 0 : Number(v) || 0);\n\nexport function summarizeSitemap(s: Sitemap): SitemapSummary {\n const contents = (s.contents ?? []).map((c) => ({\n type: c.type ?? 'web',\n submitted: num(c.submitted),\n }));\n return {\n path: s.path,\n type: s.type ?? null,\n is_index: Boolean(s.isSitemapsIndex),\n is_pending: Boolean(s.isPending),\n last_submitted: s.lastSubmitted ?? null,\n last_downloaded: s.lastDownloaded ?? null,\n errors: num(s.errors),\n warnings: num(s.warnings),\n submitted: contents.reduce((sum, c) => sum + c.submitted, 0),\n contents,\n };\n}\n\nconst INDEXED_NOTE =\n '_Google no longer reports per-sitemap indexed counts (the API field is deprecated). Use inspect_url / batch_inspect_urls to check index status of specific URLs._';\n\nfunction sitemapTable(sitemaps: SitemapSummary[]): string {\n return mdTable(\n ['Sitemap', 'Type', 'Submitted URLs', 'Errors', 'Warnings', 'Last read', 'Status'],\n sitemaps.map((s) => [\n s.path,\n s.is_index ? 'index' : (s.type ?? '—'),\n fmtInt(s.submitted),\n s.errors,\n s.warnings,\n s.last_downloaded?.slice(0, 10) ?? 'never',\n s.is_pending ? 'pending' : s.errors > 0 ? 'has errors' : 'ok',\n ]),\n );\n}\n\nexport const listSitemaps = defineTool({\n name: 'list_sitemaps',\n title: 'List sitemaps',\n description:\n 'Lists sitemaps submitted for a property with status, errors, warnings, submitted URL counts and when Google last read them. Pass sitemap_index to list the children of a sitemap index.',\n input: z.object({\n site_url: siteUrlField,\n sitemap_index: z\n .string()\n .url()\n .optional()\n .describe('Full URL of a sitemap index to list its child sitemaps.'),\n }),\n annotations: READ_ONLY,\n async run(ctx, input) {\n const property = await propertyFor(ctx, input.site_url);\n const sitemaps = await ctx.gsc.listSitemaps(property.site_url, input.sitemap_index);\n return { site_url: property.site_url, sitemaps: sitemaps.map(summarizeSitemap) };\n },\n format(r) {\n if (r.sitemaps.length === 0) {\n return `No sitemaps are submitted for ${r.site_url}. Submit one in Search Console → Sitemaps (or with submit_sitemap if writes are enabled).`;\n }\n const withErrors = r.sitemaps.filter((s) => s.errors > 0).length;\n return sections(\n `**${r.sitemaps.length} sitemap(s)** for ${r.site_url}${withErrors ? ` — ${withErrors} with errors` : ''}:`,\n sitemapTable(r.sitemaps),\n INDEXED_NOTE,\n );\n },\n});\n\nexport const getSitemap = defineTool({\n name: 'get_sitemap',\n title: 'Get sitemap details',\n description:\n 'Shows one sitemap: status, errors, warnings, submitted URL counts per content type and last download time.',\n input: z.object({\n site_url: siteUrlField,\n sitemap_url: z\n .string()\n .url()\n .describe('Full sitemap URL exactly as submitted (see list_sitemaps).'),\n }),\n annotations: READ_ONLY,\n async run(ctx, input) {\n const property = await propertyFor(ctx, input.site_url);\n return {\n site_url: property.site_url,\n sitemap: summarizeSitemap(await ctx.gsc.getSitemap(property.site_url, input.sitemap_url)),\n };\n },\n format(r) {\n const s = r.sitemap;\n const contents = s.contents.map((c) => `${c.type}: ${fmtInt(c.submitted)}`).join(' · ');\n return sections(\n `**Sitemap** ${s.path}`,\n [\n `- Type: ${s.is_index ? 'sitemap index' : (s.type ?? 'unknown')}${s.is_pending ? ' (processing pending)' : ''}`,\n `- Submitted URLs: ${fmtInt(s.submitted)}${contents ? ` (${contents})` : ''}`,\n `- Errors: ${s.errors} · Warnings: ${s.warnings}`,\n `- Last submitted: ${s.last_submitted ?? 'unknown'} · Last read by Google: ${s.last_downloaded ?? 'never'}`,\n ].join('\\n'),\n INDEXED_NOTE,\n );\n },\n});\n\nconst WRITE_NOTE =\n 'Requires GSCLAW_ALLOW_WRITES=true and Full (not Restricted) permission on the property.';\n\nexport const submitSitemap = defineTool({\n name: 'submit_sitemap',\n title: 'Submit sitemap (writes to Search Console)',\n description: `MUTATES STATE: submits (or resubmits) a sitemap URL to Search Console for the property, asking Google to crawl it. ${WRITE_NOTE}`,\n input: z.object({\n site_url: siteUrlField,\n sitemap_url: z.string().url().describe('Full sitemap URL to submit. Must be on the property.'),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n },\n write: true,\n async run(ctx, input) {\n const property = await propertyFor(ctx, input.site_url);\n if (property.permission_level === 'restricted') {\n throw new UserFacingError(\n `Restricted users cannot submit sitemaps. Give ${ctx.gsc.identity.email} Full permission on '${property.site_url}' in Search Console → Settings → Users and permissions.`,\n 'permission',\n );\n }\n await ctx.gsc.submitSitemap(property.site_url, input.sitemap_url);\n return { site_url: property.site_url, sitemap_url: input.sitemap_url, submitted: true };\n },\n format(r) {\n return `Submitted ${r.sitemap_url} for ${r.site_url}. Google will fetch it shortly; check status later with get_sitemap.`;\n },\n});\n\nexport const deleteSitemap = defineTool({\n name: 'delete_sitemap',\n title: 'Delete sitemap (writes to Search Console)',\n description: `MUTATES STATE (destructive): removes a submitted sitemap from Search Console for the property. This does not delete the file from your site. ${WRITE_NOTE}`,\n input: z.object({\n site_url: siteUrlField,\n sitemap_url: z\n .string()\n .url()\n .describe('Full sitemap URL to remove, exactly as listed by list_sitemaps.'),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: false,\n },\n write: true,\n async run(ctx, input) {\n const property = await propertyFor(ctx, input.site_url);\n await ctx.gsc.deleteSitemap(property.site_url, input.sitemap_url);\n return { site_url: property.site_url, sitemap_url: input.sitemap_url, deleted: true };\n },\n format(r) {\n return `Removed ${r.sitemap_url} from Search Console for ${r.site_url}. The file on your site is unchanged.`;\n },\n});\n","import { comparePeriods } from './compare.js';\nimport { contentDecay } from './content-decay.js';\nimport { batchInspectUrls, inspectUrl } from './inspection.js';\nimport {\n ctrOpportunities,\n keywordCannibalization,\n strikingDistanceKeywords,\n} from './opportunities.js';\nimport { pageReport } from './page-report.js';\nimport { searchAnalytics } from './search-analytics.js';\nimport { listSites } from './sites.js';\nimport { deleteSitemap, getSitemap, listSitemaps, submitSitemap } from './sitemaps.js';\nimport type { AnyTool } from './types.js';\n\n/** Every tool, in the order clients list them. */\nexport const ALL_TOOLS: AnyTool[] = [\n listSites,\n searchAnalytics,\n comparePeriods,\n strikingDistanceKeywords,\n ctrOpportunities,\n keywordCannibalization,\n contentDecay,\n pageReport,\n inspectUrl,\n batchInspectUrls,\n listSitemaps,\n getSitemap,\n submitSitemap,\n deleteSitemap,\n] as AnyTool[];\n\n/** Tools available for a request: write tools only when enabled and the credentials can write. */\nexport function availableTools(opts: { allowWrites: boolean; canWrite: boolean }): AnyTool[] {\n return ALL_TOOLS.filter((tool) => !tool.write || (opts.allowWrites && opts.canWrite));\n}\n\nexport function findTool(name: string): AnyTool | undefined {\n return ALL_TOOLS.find((tool) => tool.name === name);\n}\n","import { ActivityLog } from './activity.js';\nimport type { Config } from './config.js';\nimport { GscClient, createResponseCache } from './gsc/client.js';\nimport {\n SCOPE_READONLY,\n SCOPE_READWRITE,\n ServiceAccountTokenSource,\n UserTokenSource,\n type UserTokenCache,\n} from './gsc/auth.js';\nimport { createLogger, type Logger, type LogWriter } from './log.js';\nimport { refreshGoogleToken } from './oauth/google.js';\nimport { SCOPE_WRITE, type Principal } from './principal.js';\nimport { availableTools } from './tools/registry.js';\nimport type { AnyTool, ToolContext } from './tools/types.js';\n\nexport interface RuntimeOptions {\n /** Outbound fetch (tests inject a mock Google API here). */\n fetch?: typeof fetch;\n logWriter?: LogWriter;\n now?: () => Date;\n logFields?: Record<string, unknown>;\n}\n\n/** What one caller can do: a Search Console client for their identity and the tools they may use. */\nexport interface Session {\n gsc: GscClient;\n ctx: ToolContext;\n tools: AnyTool[];\n}\n\n/** Everything a transport needs to serve tools for one configured deployment. */\nexport interface Runtime {\n config: Config;\n logger: Logger;\n activity: ActivityLog;\n /** Service-account mode: the deployment owner's session (also used by stdio). */\n owner?: Session;\n /** The session for an authenticated caller. */\n sessionFor(principal: Principal): Session;\n}\n\nexport function createRuntime(config: Config, opts: RuntimeOptions = {}): Runtime {\n const logger = createLogger({\n level: config.logLevel,\n write: opts.logWriter,\n base: opts.logFields,\n });\n const now = opts.now ?? (() => new Date());\n const cache = createResponseCache();\n const userTokens: UserTokenCache = new Map();\n\n const sessionWith = (gsc: GscClient, principal?: Principal): Session => {\n const canWrite = gsc.canWrite && (!principal || principal.scopes.includes(SCOPE_WRITE));\n return {\n gsc,\n ctx: { gsc, config, logger, now },\n tools: availableTools({ allowWrites: config.allowWrites, canWrite }),\n };\n };\n\n let owner: Session | undefined;\n if (config.authMode === 'service_account') {\n if (!config.serviceAccount)\n throw new Error('Service-account mode requires a service account key.');\n const scopes = [config.allowWrites ? SCOPE_READWRITE : SCOPE_READONLY];\n const tokenSource = new ServiceAccountTokenSource(config.serviceAccount, scopes, {\n fetch: opts.fetch,\n });\n owner = sessionWith(\n new GscClient({\n tokenSource,\n logger,\n fetch: opts.fetch,\n cache,\n cacheTtlMs: config.cacheTtlSeconds * 1000,\n }),\n );\n }\n\n return {\n config,\n logger,\n activity: new ActivityLog(),\n owner,\n sessionFor(principal) {\n if (config.authMode === 'service_account') return sessionWith(owner!.gsc, principal);\n\n const google = principal.google;\n const oauth = config.googleOAuth;\n if (principal.kind !== 'user' || !google || !principal.email || !oauth) {\n throw new Error('OAuth mode requires a signed-in Google user.');\n }\n const tokenSource = new UserTokenSource(principal.email, google.scope, {\n sub: principal.sub,\n accessToken: google.accessToken,\n expiresAt: google.expiresAt,\n refreshToken: google.refreshToken,\n cache: userTokens,\n now: () => now().getTime(),\n refresh: async (refreshToken) => {\n const fresh = await refreshGoogleToken(oauth, {\n refreshToken,\n fetch: opts.fetch ?? ((input, init) => fetch(input, init)),\n nowSeconds: Math.floor(now().getTime() / 1000),\n });\n return { accessToken: fresh.accessToken, expiresAt: fresh.expiresAt };\n },\n });\n return sessionWith(\n new GscClient({\n tokenSource,\n logger,\n fetch: opts.fetch,\n cache,\n cacheTtlMs: config.cacheTtlSeconds * 1000,\n }),\n principal,\n );\n },\n };\n}\n","import { createMcpHandler, type McpHttpHandler } from '@modelcontextprotocol/server';\nimport { clientLabel } from './activity.js';\nimport { loadConfig, type ConfigIssue, type Env } from './config.js';\nimport { timingSafeEqual } from './crypto.js';\nimport { createDashboardApi } from './dashboard-api.js';\nimport { createOAuthServer } from './oauth/server.js';\nimport { OWNER, type Principal } from './principal.js';\nimport { createSealer } from './seal.js';\nimport { DASHBOARD_HEADERS, dashboardShell } from './dashboard-shell.js';\nimport { createLogger, type Logger, type LogWriter } from './log.js';\nimport { buildMcpServer } from './mcp.js';\nimport { HTML_SECURITY_HEADERS, runningPage, setupPage } from './pages.js';\nimport { RateLimiter } from './ratelimit.js';\nimport { createRuntime, type Runtime } from './runtime.js';\nimport { VERSION } from './version.js';\n\nexport interface PlatformOptions {\n name: 'node' | 'vercel' | 'netlify' | 'cloudflare' | 'test';\n /** Outbound fetch for Google APIs (tests inject a mock). */\n fetch?: typeof fetch;\n logWriter?: LogWriter;\n /** Server is bound to loopback: enforce Host-header checks and allow localhost origins. */\n localOnly?: boolean;\n now?: () => Date;\n /**\n * Serves built static files (dashboard assets) for adapters without a platform CDN layer.\n * Returns null when no file matches.\n */\n serveStatic?: (request: Request) => Promise<Response | null>;\n}\n\nexport interface App {\n fetch(request: Request): Promise<Response>;\n /** Configuration problems; non-empty means the app only serves the setup page. */\n readonly setupErrors: ConfigIssue[];\n readonly warnings: string[];\n readonly runtime: Runtime | null;\n}\n\nconst MAX_BODY_BYTES = 1_000_000;\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);\nconst MCP_CORS_HEADERS = {\n 'access-control-allow-methods': 'POST, GET, DELETE, OPTIONS',\n 'access-control-allow-headers':\n 'authorization, content-type, accept, mcp-protocol-version, mcp-method, mcp-name, mcp-session-id, last-event-id',\n 'access-control-expose-headers': 'www-authenticate, mcp-protocol-version, mcp-session-id',\n 'access-control-max-age': '600',\n};\n\nfunction json(body: unknown, status = 200, headers: Record<string, string> = {}): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json', 'cache-control': 'no-store', ...headers },\n });\n}\n\nfunction html(body: string, status = 200): Response {\n return new Response(body, { status, headers: HTML_SECURITY_HEADERS });\n}\n\n/** Malformed percent-encoding must look like any other wrong secret (404), not a 500. */\nfunction safeDecode(segment: string): string {\n try {\n return decodeURIComponent(segment);\n } catch {\n return segment;\n }\n}\n\n/** Deployments are private endpoints; ask crawlers not to index them. */\nconst robots = () =>\n new Response('User-agent: *\\nDisallow: /\\n', {\n headers: {\n 'content-type': 'text/plain; charset=utf-8',\n 'cache-control': 'public, max-age=86400',\n },\n });\n\nconst notFound = () =>\n new Response('Not found', { status: 404, headers: { 'content-type': 'text/plain' } });\n\nfunction hostnameOf(hostHeader: string | null): string | null {\n if (!hostHeader) return null;\n try {\n return new URL(`http://${hostHeader}`).hostname;\n } catch {\n return null;\n }\n}\n\n/** Builds the web-standard request handler shared by every adapter. */\nexport function createApp(env: Env, platform: PlatformOptions): App {\n const loaded = loadConfig(env, { transport: 'http' });\n if (!loaded.ok) {\n const logger = createLogger({ write: platform.logWriter, base: { platform: platform.name } });\n logger.error('configuration invalid; serving setup page only', {\n issues: loaded.errors.map((e) => `${e.variable ?? ''} ${e.message}`.trim()),\n });\n return {\n setupErrors: loaded.errors,\n warnings: [],\n runtime: null,\n async fetch(request) {\n const path = new URL(request.url).pathname;\n if (path === '/healthz') return json({ status: 'setup_required', version: VERSION }, 503);\n if (path === '/robots.txt') return robots();\n if (path === '/' && (request.method === 'GET' || request.method === 'HEAD')) {\n return html(setupPage(loaded.errors), 503);\n }\n return json(\n {\n error: 'setup_required',\n error_description: 'This GSClaw deployment is not configured.',\n },\n 503,\n );\n },\n };\n }\n\n const { config } = loaded;\n const runtime = createRuntime(config, {\n fetch: platform.fetch,\n logWriter: platform.logWriter,\n now: platform.now,\n logFields: { platform: platform.name },\n });\n const { logger } = runtime;\n for (const warning of loaded.warnings) logger.warn(warning);\n\n const now = platform.now ?? (() => new Date());\n const oauth = createOAuthServer({\n config,\n sealer: createSealer(config.sealingSecret!, () => now().getTime()),\n logger,\n fetch: platform.fetch ?? ((input, init) => fetch(input, init)),\n now,\n });\n const limiter = new RateLimiter(config.rateLimitPerMinute);\n const publicOrigin = config.publicBaseUrl ? new URL(config.publicBaseUrl).origin : null;\n\n /**\n * Public base URL for OAuth metadata and token audiences: PUBLIC_BASE_URL (or the platform's\n * detected URL), else the forwarded or request origin.\n */\n const baseUrl = (request: Request, url: URL): string => {\n if (config.publicBaseUrl) return config.publicBaseUrl;\n const proto = request.headers.get('x-forwarded-proto')?.split(',')[0]?.trim();\n const host = request.headers.get('x-forwarded-host')?.split(',')[0]?.trim();\n if (proto && host && /^https?$/.test(proto)) return `${proto}://${host}`;\n return url.origin;\n };\n\n const dashboardApi = config.dashboard\n ? createDashboardApi({\n config,\n runtime,\n oauth,\n logger,\n platform: platform.name,\n fetch: platform.fetch,\n now: platform.now,\n baseUrl,\n })\n : null;\n\n const mcp: McpHttpHandler = createMcpHandler(\n ({ authInfo }) => {\n const principal = authInfo?.extra?.principal as Principal;\n const session = runtime.sessionFor(principal);\n return buildMcpServer({\n tools: session.tools,\n ctx: session.ctx,\n logger,\n activity: runtime.activity,\n client: typeof authInfo?.extra?.client === 'string' ? authInfo.extra.client : 'unknown',\n user: principal.kind === 'user' ? principal.email : undefined,\n });\n },\n {\n legacy: 'stateless',\n responseMode: 'auto',\n onerror: (error) => logger.debug('mcp transport error', { error }),\n },\n );\n\n const originAllowed = (origin: string, requestUrl: URL): boolean => {\n if (origin === requestUrl.origin || origin === publicOrigin) return true;\n if (config.allowedOrigins.includes(origin)) return true;\n if (platform.localOnly) {\n const host = hostnameOf(origin.replace(/^https?:\\/\\//, ''));\n return host !== null && LOOPBACK_HOSTS.has(host);\n }\n return false;\n };\n\n const corsHeaders = (origin: string | null): Record<string, string> =>\n origin ? { 'access-control-allow-origin': origin, vary: 'Origin', ...MCP_CORS_HEADERS } : {};\n\n /**\n * Resolves the caller of /mcp. Service-account mode: the static access token, the secret path, or\n * a GSClaw-issued OAuth token (owner sign-in). OAuth mode: a GSClaw-issued token for a Google user.\n */\n async function authenticate(\n request: Request,\n path: string,\n base: string,\n ): Promise<Principal | Response> {\n if (path !== '/mcp') {\n const secret = path.slice('/mcp/'.length);\n // Wrong secret paths are indistinguishable from any other unknown URL.\n if (\n config.authMode !== 'service_account' ||\n !config.secretPath ||\n secret.includes('/') ||\n !(await timingSafeEqual(safeDecode(secret), config.accessToken!))\n ) {\n return notFound();\n }\n return { ...OWNER, via: 'secret_path' };\n }\n\n const header = request.headers.get('authorization') ?? '';\n const token = /^Bearer\\s+(.+)$/i.exec(header)?.[1]?.trim();\n if (token) {\n if (\n config.authMode === 'service_account' &&\n (await timingSafeEqual(token, config.accessToken!))\n ) {\n return OWNER;\n }\n const principal = await oauth.verifyAccessToken(token, base);\n if (principal) return principal;\n }\n const hint =\n config.authMode === 'oauth'\n ? 'Sign in with OAuth (your MCP client does this automatically).'\n : \"Send 'Authorization: Bearer <GSCLAW_ACCESS_TOKEN>' or sign in with OAuth.\";\n return json(\n { error: 'invalid_token', error_description: `Missing or invalid credentials. ${hint}` },\n 401,\n {\n 'www-authenticate': oauth.challenge(base, token ? 'invalid_token' : undefined),\n },\n );\n }\n\n async function handleMcp(\n request: Request,\n url: URL,\n path: string,\n log: Logger,\n ): Promise<Response> {\n const origin = request.headers.get('origin');\n if (origin && !originAllowed(origin, url)) {\n log.warn('rejected request from disallowed origin', { origin });\n return json(\n {\n error: 'forbidden',\n error_description: 'Origin not allowed. Add it to GSCLAW_ALLOWED_ORIGINS.',\n },\n 403,\n );\n }\n if (platform.localOnly) {\n const host = hostnameOf(request.headers.get('host'));\n if (!host || !LOOPBACK_HOSTS.has(host)) {\n return json({ error: 'forbidden', error_description: 'Invalid Host header.' }, 403);\n }\n }\n if (request.method === 'OPTIONS')\n return new Response(null, { status: 204, headers: corsHeaders(origin) });\n\n const principal = await authenticate(request, path, baseUrl(request, url));\n if (principal instanceof Response) {\n for (const [k, v] of Object.entries(corsHeaders(origin))) principal.headers.set(k, v);\n return principal;\n }\n\n const limit = limiter.check(principal.sub);\n if (!limit.ok) {\n return json(\n { error: 'rate_limited', error_description: 'Too many requests. Slow down and retry.' },\n 429,\n {\n 'retry-after': String(limit.retryAfterSeconds),\n ...corsHeaders(origin),\n },\n );\n }\n\n const length = Number(request.headers.get('content-length') ?? 0);\n if (length > MAX_BODY_BYTES) return json({ error: 'payload_too_large' }, 413);\n const body = request.method === 'POST' ? await request.text() : undefined;\n if (body && body.length > MAX_BODY_BYTES) return json({ error: 'payload_too_large' }, 413);\n\n // Hand the SDK a clean request: canonical URL, no credentials.\n const headers = new Headers(request.headers);\n headers.delete('authorization');\n headers.delete('cookie');\n const clean = new Request(new URL('/mcp', url), { method: request.method, headers, body });\n\n const response = await mcp.fetch(clean, {\n authInfo: {\n token: 'redacted',\n clientId: principal.clientId ?? principal.sub,\n scopes: principal.scopes,\n extra: {\n principal,\n client: principal.clientName ?? clientLabel(request.headers.get('user-agent')),\n },\n },\n });\n const out = new Response(response.body, response);\n out.headers.set('cache-control', 'no-store');\n for (const [k, v] of Object.entries(corsHeaders(origin))) out.headers.set(k, v);\n return out;\n }\n\n async function route(request: Request, log: Logger): Promise<Response> {\n const url = new URL(request.url);\n const path = url.pathname.length > 1 ? url.pathname.replace(/\\/+$/, '') : url.pathname;\n\n if (path === '/healthz') return json({ status: 'ok', version: VERSION });\n if (path === '/robots.txt') return robots();\n if (path === '/mcp' || path.startsWith('/mcp/')) return handleMcp(request, url, path, log);\n const oauthResponse = await oauth.handle(request, url, path, baseUrl(request, url));\n if (oauthResponse) return oauthResponse;\n const isRead = request.method === 'GET' || request.method === 'HEAD';\n if (path === '/' && isRead) {\n return dashboardApi\n ? new Response(dashboardShell(), { headers: DASHBOARD_HEADERS })\n : html(runningPage());\n }\n if (path === '/api' || path.startsWith('/api/')) {\n return dashboardApi ? dashboardApi.handle(request, url, path) : notFound();\n }\n if (isRead && platform.serveStatic) {\n const file = await platform.serveStatic(request);\n if (file) return file;\n }\n return notFound();\n }\n\n return {\n setupErrors: [],\n warnings: loaded.warnings,\n runtime,\n async fetch(request) {\n const started = Date.now();\n const url = new URL(request.url);\n // Never log the secret path segment or OAuth query strings.\n const logPath = url.pathname.startsWith('/mcp/') ? '/mcp/<secret>' : url.pathname;\n const log = logger.child({ method: request.method, path: logPath });\n let response: Response;\n try {\n response = await route(request, log);\n } catch (error) {\n log.error('unhandled error', { error });\n response = json({ error: 'internal_error' }, 500);\n }\n if (logPath !== '/healthz')\n log.info('request', { status: response.status, ms: Date.now() - started });\n return response;\n },\n };\n}\n"],"mappings":"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"}