@adport/provider-google 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts","../src/provider.ts","../src/tools.ts","../src/index.ts"],"sourcesContent":["import { AdportError } from '@adport/core';\n\n/**\n * Matches the google-ads.yaml naming conventions so credentials translate 1:1.\n */\nexport interface GoogleCredentials {\n developerToken: string;\n clientId: string;\n clientSecret: string;\n refreshToken: string;\n /** Manager (MCC) customer id, digits only. */\n loginCustomerId?: string;\n}\n\nexport interface SearchOptions {\n /** Stop after this many rows. */\n maxRows?: number;\n}\n\nconst TOKEN_URL = 'https://oauth2.googleapis.com/token';\nconst API_BASE = 'https://googleads.googleapis.com';\n// Matches the API version the reference implementation pinned; bump deliberately.\nexport const DEFAULT_API_VERSION = 'v24';\n\nexport function normalizeCustomerId(id: string): string {\n const normalized = id.replace(/^customers\\//, '').replace(/-/g, '').trim();\n if (!/^\\d{10}$/.test(normalized)) {\n throw new AdportError('INVALID_INPUT', `\"${id}\" is not a valid Google Ads customer id (expected 10 digits).`);\n }\n return normalized;\n}\n\n/**\n * Minimal Google Ads REST client: OAuth token refresh + search + mutate.\n * Deliberately dependency-free — the REST interface is stable and we control\n * exactly what goes over the wire.\n */\nexport class GoogleAdsRestClient {\n private accessToken?: { token: string; expiresAt: number };\n\n constructor(\n private readonly credentials: GoogleCredentials,\n private readonly version: string = process.env.GOOGLE_ADS_API_VERSION ?? DEFAULT_API_VERSION,\n private readonly fetchImpl: typeof fetch = fetch,\n ) {}\n\n async getAccessToken(): Promise<string> {\n if (this.accessToken && this.accessToken.expiresAt > Date.now()) {\n return this.accessToken.token;\n }\n const response = await this.fetchImpl(TOKEN_URL, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n client_id: this.credentials.clientId,\n client_secret: this.credentials.clientSecret,\n refresh_token: this.credentials.refreshToken,\n grant_type: 'refresh_token',\n }),\n });\n if (!response.ok) {\n const body = await response.text();\n throw new AdportError(\n 'PROVIDER_ERROR',\n `Google OAuth token refresh failed (${response.status}). Re-run \\`adport connect google\\` if the refresh token was revoked.`,\n safeJson(body),\n );\n }\n const data = (await response.json()) as { access_token: string; expires_in: number };\n this.accessToken = { token: data.access_token, expiresAt: Date.now() + (data.expires_in - 60) * 1000 };\n return this.accessToken.token;\n }\n\n private async request<T>(path: string, init: { method: string; body?: unknown }): Promise<T> {\n const token = await this.getAccessToken();\n const headers: Record<string, string> = {\n authorization: `Bearer ${token}`,\n 'developer-token': this.credentials.developerToken,\n 'content-type': 'application/json',\n };\n if (this.credentials.loginCustomerId) {\n headers['login-customer-id'] = normalizeCustomerId(this.credentials.loginCustomerId);\n }\n const response = await this.fetchImpl(`${API_BASE}/${this.version}/${path}`, {\n method: init.method,\n headers,\n body: init.body === undefined ? undefined : JSON.stringify(init.body),\n });\n const raw = await response.text();\n if (!response.ok) {\n throw new AdportError('PROVIDER_ERROR', formatGoogleAdsError(response.status, raw), safeJson(raw));\n }\n return (raw ? JSON.parse(raw) : {}) as T;\n }\n\n async listAccessibleCustomers(): Promise<string[]> {\n const data = await this.request<{ resourceNames?: string[] }>('customers:listAccessibleCustomers', {\n method: 'GET',\n });\n return (data.resourceNames ?? []).map((name) => name.replace('customers/', ''));\n }\n\n /** Paged GAQL search; accumulates rows up to options.maxRows (default 1000). */\n async search(\n customerId: string,\n query: string,\n options: SearchOptions = {},\n ): Promise<Array<Record<string, unknown>>> {\n const maxRows = options.maxRows ?? 1000;\n const rows: Array<Record<string, unknown>> = [];\n let pageToken: string | undefined;\n do {\n const data = await this.request<{ results?: Array<Record<string, unknown>>; nextPageToken?: string }>(\n `customers/${normalizeCustomerId(customerId)}/googleAds:search`,\n { method: 'POST', body: { query, pageToken } },\n );\n rows.push(...(data.results ?? []));\n pageToken = data.nextPageToken;\n } while (pageToken && rows.length < maxRows);\n return rows.slice(0, maxRows);\n }\n\n /**\n * Service-specific mutate (campaigns, campaignBudgets, adGroups, adGroupCriteria, adGroupAds).\n * validateOnly=true is the server-side dry run.\n */\n async mutate(\n customerId: string,\n service: string,\n operations: Array<Record<string, unknown>>,\n { validateOnly }: { validateOnly: boolean },\n ): Promise<{ results?: Array<{ resourceName?: string }> }> {\n return this.request(`customers/${normalizeCustomerId(customerId)}/${service}:mutate`, {\n method: 'POST',\n body: { operations, validateOnly, partialFailure: false },\n });\n }\n\n /** Cross-service atomic mutate (e.g. budget + campaign with temp resource ids). */\n async googleAdsMutate(\n customerId: string,\n mutateOperations: Array<Record<string, unknown>>,\n { validateOnly }: { validateOnly: boolean },\n ): Promise<{ mutateOperationResponses?: Array<Record<string, { resourceName?: string }>> }> {\n return this.request(`customers/${normalizeCustomerId(customerId)}/googleAds:mutate`, {\n method: 'POST',\n body: { mutateOperations, validateOnly, partialFailure: false },\n });\n }\n}\n\nfunction safeJson(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n\n/**\n * Render Google Ads failures with field paths and request id so an agent can\n * self-correct (\"error at operations[0].create.name: ...\").\n */\nexport function formatGoogleAdsError(status: number, raw: string): string {\n const parsed = safeJson(raw) as {\n error?: {\n message?: string;\n details?: Array<{ requestId?: string; errors?: Array<GoogleAdsErrorItem> }>;\n };\n };\n const error = parsed?.error;\n if (!error) return `Google Ads API error (HTTP ${status})`;\n const lines: string[] = [];\n let requestId: string | undefined;\n for (const detail of error.details ?? []) {\n requestId = requestId ?? detail.requestId;\n for (const item of detail.errors ?? []) {\n const path = (item.location?.fieldPathElements ?? [])\n .map((el) => (el.index !== undefined ? `${el.fieldName}[${el.index}]` : el.fieldName))\n .join('.');\n lines.push(`${path ? `at ${path}: ` : ''}${item.message}`);\n }\n }\n const head = `Google Ads API error (HTTP ${status}): ${error.message ?? 'request failed'}`;\n const tail = requestId ? ` [request-id: ${requestId}]` : '';\n return lines.length > 0 ? `${head}\\n ${lines.join('\\n ')}${tail}` : `${head}${tail}`;\n}\n\ninterface GoogleAdsErrorItem {\n message: string;\n location?: { fieldPathElements?: Array<{ fieldName: string; index?: number }> };\n}\n","import {\n AdportError,\n resolveDateRange,\n type Account,\n type AdProvider,\n type MetricName,\n type NormalizedQuery,\n type ProviderCapabilities,\n type Report,\n type ReportRow,\n type WriteGuard,\n type WriteOperation,\n type WritePreview,\n type WriteResult,\n} from '@adport/core';\nimport { GoogleAdsRestClient, normalizeCustomerId } from './client.js';\n\nconst LEVEL_RESOURCE = {\n account: 'customer',\n campaign: 'campaign',\n ad_group: 'ad_group',\n ad: 'ad_group_ad',\n} as const;\n\n/** Base metrics fetched from the API; ratio metrics are derived client-side for consistency. */\nconst BASE_METRIC_FIELDS: Record<string, string> = {\n spend: 'metrics.cost_micros',\n impressions: 'metrics.impressions',\n clicks: 'metrics.clicks',\n conversions: 'metrics.conversions',\n conversion_value: 'metrics.conversions_value',\n};\n\ninterface WritePlan {\n summary: string;\n changes: string[];\n coercions: string[];\n budgetDeltas: WritePreview['budgetDeltas'];\n execute: (validateOnly: boolean) => Promise<string[]>;\n}\n\nexport class GoogleAdsProvider implements AdProvider {\n readonly id = 'google';\n\n constructor(private readonly client: GoogleAdsRestClient) {}\n\n capabilities(): ProviderCapabilities {\n return { serverDryRun: true };\n }\n\n async listAccounts(): Promise<Account[]> {\n const ids = await this.client.listAccessibleCustomers();\n const accounts: Account[] = [];\n for (const id of ids) {\n try {\n const rows = await this.client.search(\n id,\n 'SELECT customer.id, customer.descriptive_name, customer.currency_code, customer.status, customer.manager FROM customer LIMIT 1',\n { maxRows: 1 },\n );\n const customer = (rows[0]?.customer ?? {}) as {\n descriptiveName?: string;\n currencyCode?: string;\n status?: string;\n manager?: boolean;\n };\n accounts.push({\n provider: this.id,\n id,\n name: customer.descriptiveName ?? `(account ${id})`,\n currency: customer.currencyCode,\n status: customer.manager ? `${customer.status ?? 'UNKNOWN'} (manager)` : customer.status,\n });\n } catch {\n accounts.push({ provider: this.id, id, name: '(details unavailable)', status: 'UNKNOWN' });\n }\n }\n return accounts;\n }\n\n async report(query: NormalizedQuery): Promise<Report> {\n const range = resolveDateRange(query.dateRange);\n const resource = LEVEL_RESOURCE[query.level];\n const accountIds =\n query.accountIds ??\n (await this.listAccounts()).filter((a) => !a.status?.includes('manager')).map((a) => a.id);\n\n const baseMetrics = new Set<string>();\n for (const metric of query.metrics) {\n const field = BASE_METRIC_FIELDS[metric];\n if (field) baseMetrics.add(field);\n }\n // Ratio metrics need their inputs.\n const wants = (m: MetricName) => query.metrics.includes(m);\n if (wants('ctr')) ['metrics.clicks', 'metrics.impressions'].forEach((f) => baseMetrics.add(f));\n if (wants('cpc') || wants('cpa') || wants('cpm') || wants('roas')) baseMetrics.add('metrics.cost_micros');\n if (wants('cpc')) baseMetrics.add('metrics.clicks');\n if (wants('cpm')) baseMetrics.add('metrics.impressions');\n if (wants('cpa')) baseMetrics.add('metrics.conversions');\n if (wants('roas')) baseMetrics.add('metrics.conversions_value');\n\n const entityFields = {\n account: ['customer.id', 'customer.descriptive_name', 'customer.status'],\n campaign: ['campaign.id', 'campaign.name', 'campaign.status'],\n ad_group: ['ad_group.id', 'ad_group.name', 'ad_group.status', 'campaign.name'],\n ad: ['ad_group_ad.ad.id', 'ad_group_ad.status', 'ad_group.name'],\n }[query.level];\n\n const gaql =\n `SELECT ${[...entityFields, ...baseMetrics].join(', ')} FROM ${resource} ` +\n `WHERE segments.date BETWEEN '${range.start}' AND '${range.end}' ` +\n `PARAMETERS omit_unselected_resource_names=true`;\n\n const rows: ReportRow[] = [];\n for (const accountId of accountIds) {\n const results = await this.client.search(accountId, gaql, { maxRows: query.limit ?? 1000 });\n for (const row of results) {\n rows.push(this.toReportRow(row, accountId, query));\n }\n }\n return { rows };\n }\n\n private toReportRow(row: Record<string, unknown>, accountId: string, query: NormalizedQuery): ReportRow {\n const metricsRaw = (row.metrics ?? {}) as Record<string, string | number>;\n const num = (key: string) => Number(metricsRaw[key] ?? 0);\n const spend = num('costMicros') / 1_000_000;\n const impressions = num('impressions');\n const clicks = num('clicks');\n const conversions = num('conversions');\n const conversionValue = num('conversionsValue');\n const all: Record<MetricName, number> = {\n spend: round2(spend),\n impressions,\n clicks,\n conversions,\n conversion_value: round2(conversionValue),\n ctr: impressions > 0 ? round2((clicks / impressions) * 100) : 0,\n cpc: clicks > 0 ? round2(spend / clicks) : 0,\n cpm: impressions > 0 ? round2((spend / impressions) * 1000) : 0,\n cpa: conversions > 0 ? round2(spend / conversions) : 0,\n roas: spend > 0 ? round2(conversionValue / spend) : 0,\n };\n\n let entity: ReportRow['entity'];\n switch (query.level) {\n case 'account': {\n const customer = (row.customer ?? {}) as { id?: string; descriptiveName?: string; status?: string };\n entity = { level: 'account', id: String(customer.id ?? accountId), name: customer.descriptiveName ?? accountId, status: customer.status };\n break;\n }\n case 'campaign': {\n const campaign = (row.campaign ?? {}) as { id?: string; name?: string; status?: string };\n entity = { level: 'campaign', id: String(campaign.id ?? ''), name: campaign.name ?? '', status: campaign.status };\n break;\n }\n case 'ad_group': {\n const adGroup = (row.adGroup ?? {}) as { id?: string; name?: string; status?: string };\n entity = { level: 'ad_group', id: String(adGroup.id ?? ''), name: adGroup.name ?? '', status: adGroup.status };\n break;\n }\n case 'ad': {\n const adGroupAd = (row.adGroupAd ?? {}) as { ad?: { id?: string }; status?: string };\n entity = { level: 'ad', id: String(adGroupAd.ad?.id ?? ''), name: `ad ${adGroupAd.ad?.id ?? '?'}`, status: adGroupAd.status };\n break;\n }\n }\n return {\n provider: this.id,\n accountId,\n entity,\n metrics: Object.fromEntries(query.metrics.map((m) => [m, all[m]])),\n };\n }\n\n async gaqlSearch(input: {\n customer_id: string;\n resource: string;\n fields: string[];\n conditions?: string[];\n order_by?: string[];\n limit?: number;\n }): Promise<Array<Record<string, unknown>>> {\n const limit = Math.min(input.limit ?? 200, 10_000);\n let query = `SELECT ${input.fields.join(', ')} FROM ${input.resource}`;\n if (input.conditions?.length) query += ` WHERE ${input.conditions.join(' AND ')}`;\n if (input.order_by?.length) query += ` ORDER BY ${input.order_by.join(', ')}`;\n query += ` LIMIT ${limit} PARAMETERS omit_unselected_resource_names=true`;\n return this.client.search(input.customer_id, query, { maxRows: limit });\n }\n\n async previewWrite(op: WriteOperation, guard: WriteGuard): Promise<WritePreview> {\n const plan = await this.plan(op, guard);\n await plan.execute(true); // server-side dry run — throws on invalid operations\n return {\n summary: plan.summary,\n changes: plan.changes,\n coercions: plan.coercions,\n budgetDeltas: plan.budgetDeltas,\n serverValidated: true,\n };\n }\n\n async applyWrite(op: WriteOperation, guard: WriteGuard): Promise<WriteResult> {\n const plan = await this.plan(op, guard);\n const resourceIds = await plan.execute(false);\n return { applied: true, resourceIds };\n }\n\n // ---- write planning ------------------------------------------------------\n\n private async plan(op: WriteOperation, guard: WriteGuard): Promise<WritePlan> {\n const cid = normalizeCustomerId(op.accountId);\n // Tool input was already zod-validated at the registry boundary.\n const payload = op.payload as never;\n switch (op.tool) {\n case 'google_create_campaign':\n return this.planCreateCampaign(cid, payload, guard);\n case 'google_set_campaign_status':\n return this.planSetCampaignStatus(cid, payload);\n case 'google_set_budget':\n return this.planSetBudget(cid, payload);\n case 'google_create_ad_group':\n return this.planCreateAdGroup(cid, payload);\n case 'google_set_ad_group_status':\n return this.planSetAdGroupStatus(cid, payload);\n case 'google_add_keywords':\n return this.planAddKeywords(cid, payload);\n case 'google_set_keyword_status':\n return this.planSetKeywordStatus(cid, payload);\n case 'google_remove_keywords':\n return this.planRemoveKeywords(cid, payload);\n case 'google_create_responsive_search_ad':\n return this.planCreateRsa(cid, payload, guard);\n case 'google_set_bid_ceiling':\n return this.planSetBidCeiling(cid, payload);\n case 'google_set_bidding_strategy':\n return this.planSetBiddingStrategy(cid, payload);\n default:\n throw new AdportError('PROVIDER_ERROR', `google: unsupported write tool ${op.tool}`);\n }\n }\n\n private async planCreateCampaign(\n cid: string,\n payload: { name: string; daily_budget_micros: number; channel_type?: string; status?: string },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n const coercions: string[] = [];\n let status = payload.status ?? 'ENABLED';\n if (guard.forcePausedCreation && status === 'ENABLED') {\n status = 'PAUSED';\n coercions.push('status coerced to PAUSED by policy (paused_creation)');\n }\n const channelType = payload.channel_type ?? 'SEARCH';\n const budgetTempResource = `customers/${cid}/campaignBudgets/-1`;\n const mutateOperations = [\n {\n campaignBudgetOperation: {\n create: {\n resourceName: budgetTempResource,\n name: `Budget for ${payload.name} (${Date.now()})`,\n amountMicros: String(payload.daily_budget_micros),\n deliveryMethod: 'STANDARD',\n explicitlyShared: false,\n },\n },\n },\n {\n campaignOperation: {\n create: {\n name: payload.name,\n status,\n advertisingChannelType: channelType,\n manualCpc: {},\n campaignBudget: budgetTempResource,\n },\n },\n },\n ];\n return {\n summary: `Create ${channelType} campaign \"${payload.name}\" (${status}), daily budget ${payload.daily_budget_micros} micros`,\n changes: [\n `+ campaign_budget ${payload.daily_budget_micros} micros/day (not shared)`,\n `+ campaign \"${payload.name}\" status=${status} channel=${channelType} bidding=manual_cpc`,\n ],\n coercions,\n budgetDeltas: [{ target: `new campaign \"${payload.name}\" daily budget`, toMicros: payload.daily_budget_micros }],\n execute: async (validateOnly) => {\n const res = await this.client.googleAdsMutate(cid, mutateOperations, { validateOnly });\n return (res.mutateOperationResponses ?? [])\n .flatMap((r) => Object.values(r).map((v) => v.resourceName))\n .filter((r): r is string => Boolean(r));\n },\n };\n }\n\n private async planSetCampaignStatus(\n cid: string,\n payload: { campaign_id: string; status: 'ENABLED' | 'PAUSED' },\n ): Promise<WritePlan> {\n const current = await this.lookupCampaign(cid, payload.campaign_id);\n const resourceName = `customers/${cid}/campaigns/${payload.campaign_id}`;\n return {\n summary: `Set campaign \"${current.name}\" status ${current.status} → ${payload.status}`,\n changes: [`~ campaign ${payload.campaign_id} status ${current.status} → ${payload.status}`],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'campaigns',\n [{ update: { resourceName, status: payload.status }, updateMask: 'status' }],\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? resourceName);\n },\n };\n }\n\n private async planSetBudget(\n cid: string,\n payload: { campaign_id: string; daily_budget_micros: number },\n ): Promise<WritePlan> {\n const current = await this.lookupCampaign(cid, payload.campaign_id);\n const changes = [\n `~ campaign_budget ${current.budgetResource} amount ${current.budgetMicros} → ${payload.daily_budget_micros}`,\n ];\n if (current.budgetShared) {\n changes.push('! this budget is SHARED — the change affects every campaign using it');\n }\n return {\n summary: `Change \"${current.name}\" daily budget ${current.budgetMicros} → ${payload.daily_budget_micros} micros`,\n changes,\n coercions: [],\n budgetDeltas: [\n {\n target: `campaign \"${current.name}\" daily budget`,\n fromMicros: current.budgetMicros,\n toMicros: payload.daily_budget_micros,\n },\n ],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'campaignBudgets',\n [\n {\n update: {\n resourceName: current.budgetResource,\n amountMicros: String(payload.daily_budget_micros),\n },\n updateMask: 'amount_micros',\n },\n ],\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? current.budgetResource);\n },\n };\n }\n\n private async planCreateAdGroup(\n cid: string,\n payload: { campaign_id: string; name: string; cpc_bid_micros?: number },\n ): Promise<WritePlan> {\n const campaign = await this.lookupCampaign(cid, payload.campaign_id);\n const create: Record<string, unknown> = {\n name: payload.name,\n campaign: `customers/${cid}/campaigns/${payload.campaign_id}`,\n status: 'ENABLED',\n type: 'SEARCH_STANDARD',\n };\n if (payload.cpc_bid_micros) create.cpcBidMicros = String(payload.cpc_bid_micros);\n return {\n summary: `Create ad group \"${payload.name}\" in campaign \"${campaign.name}\"`,\n changes: [`+ ad_group \"${payload.name}\" in campaign ${payload.campaign_id} (ENABLED — inherits campaign state)`],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(cid, 'adGroups', [{ create }], { validateOnly });\n return (res.results ?? []).map((r) => r.resourceName ?? '');\n },\n };\n }\n\n private async planSetAdGroupStatus(\n cid: string,\n payload: { ad_group_id: string; status: 'ENABLED' | 'PAUSED' },\n ): Promise<WritePlan> {\n const resourceName = `customers/${cid}/adGroups/${payload.ad_group_id}`;\n return {\n summary: `Set ad group ${payload.ad_group_id} status → ${payload.status}`,\n changes: [`~ ad_group ${payload.ad_group_id} status → ${payload.status}`],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'adGroups',\n [{ update: { resourceName, status: payload.status }, updateMask: 'status' }],\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? resourceName);\n },\n };\n }\n\n private async planAddKeywords(\n cid: string,\n payload: { ad_group_id: string; keywords: Array<{ text: string; match_type: string }>; negative?: boolean },\n ): Promise<WritePlan> {\n const operations = payload.keywords.map((kw) => ({\n create: {\n adGroup: `customers/${cid}/adGroups/${payload.ad_group_id}`,\n status: 'ENABLED',\n negative: payload.negative ?? false,\n keyword: { text: kw.text, matchType: kw.match_type },\n },\n }));\n const kind = payload.negative ? 'negative keywords' : 'keywords';\n return {\n summary: `Add ${payload.keywords.length} ${kind} to ad group ${payload.ad_group_id}`,\n changes: payload.keywords.map((kw) => `+ ${payload.negative ? 'negative ' : ''}keyword [${kw.match_type}] \"${kw.text}\"`),\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(cid, 'adGroupCriteria', operations, { validateOnly });\n return (res.results ?? []).map((r) => r.resourceName ?? '');\n },\n };\n }\n\n private async planSetKeywordStatus(\n cid: string,\n payload: { ad_group_id: string; criterion_id: string; status: 'ENABLED' | 'PAUSED' },\n ): Promise<WritePlan> {\n const resourceName = `customers/${cid}/adGroupCriteria/${payload.ad_group_id}~${payload.criterion_id}`;\n return {\n summary: `Set keyword criterion ${payload.criterion_id} status → ${payload.status}`,\n changes: [`~ ad_group_criterion ${payload.ad_group_id}~${payload.criterion_id} status → ${payload.status}`],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'adGroupCriteria',\n [{ update: { resourceName, status: payload.status }, updateMask: 'status' }],\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? resourceName);\n },\n };\n }\n\n private async planRemoveKeywords(\n cid: string,\n payload: { ad_group_id: string; criterion_ids: string[] },\n ): Promise<WritePlan> {\n const resourceNames = payload.criterion_ids.map(\n (id) => `customers/${cid}/adGroupCriteria/${payload.ad_group_id}~${id}`,\n );\n return {\n summary: `PERMANENTLY remove ${resourceNames.length} keyword criteria from ad group ${payload.ad_group_id}`,\n changes: resourceNames.map((r) => `- ${r}`),\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'adGroupCriteria',\n resourceNames.map((remove) => ({ remove })),\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? '');\n },\n };\n }\n\n private async planCreateRsa(\n cid: string,\n payload: {\n ad_group_id: string;\n headlines: string[];\n descriptions: string[];\n final_urls: string[];\n path1?: string;\n path2?: string;\n },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n validateRsa(payload);\n const coercions: string[] = [];\n // RSAs always start paused (matches the reference implementation) unless policy says otherwise.\n const status = guard.forcePausedCreation ? 'PAUSED' : 'ENABLED';\n if (guard.forcePausedCreation) coercions.push('ad created PAUSED by policy (paused_creation)');\n const create = {\n adGroup: `customers/${cid}/adGroups/${payload.ad_group_id}`,\n status,\n ad: {\n finalUrls: payload.final_urls,\n responsiveSearchAd: {\n headlines: payload.headlines.map((text) => ({ text })),\n descriptions: payload.descriptions.map((text) => ({ text })),\n ...(payload.path1 ? { path1: payload.path1 } : {}),\n ...(payload.path2 ? { path2: payload.path2 } : {}),\n },\n },\n };\n return {\n summary: `Create responsive search ad (${payload.headlines.length} headlines, ${payload.descriptions.length} descriptions) in ad group ${payload.ad_group_id}`,\n changes: [`+ responsive_search_ad in ad_group ${payload.ad_group_id} status=${status}`],\n coercions,\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(cid, 'adGroupAds', [{ create }], { validateOnly });\n return (res.results ?? []).map((r) => r.resourceName ?? '');\n },\n };\n }\n\n /** CPC ceilings exist on TARGET_SPEND (Maximize clicks) and TARGET_IMPRESSION_SHARE. */\n private async planSetBidCeiling(\n cid: string,\n payload: { campaign_id: string; cpc_bid_ceiling_micros: number },\n ): Promise<WritePlan> {\n const current = await this.lookupCampaignBidding(cid, payload.campaign_id);\n const resourceName = `customers/${cid}/campaigns/${payload.campaign_id}`;\n let update: Record<string, unknown>;\n let updateMask: string;\n if (current.strategyType === 'TARGET_SPEND') {\n update = { resourceName, targetSpend: { cpcBidCeilingMicros: String(payload.cpc_bid_ceiling_micros) } };\n updateMask = 'target_spend.cpc_bid_ceiling_micros';\n } else if (current.strategyType === 'TARGET_IMPRESSION_SHARE') {\n update = {\n resourceName,\n targetImpressionShare: { cpcBidCeilingMicros: String(payload.cpc_bid_ceiling_micros) },\n };\n updateMask = 'target_impression_share.cpc_bid_ceiling_micros';\n } else {\n throw new AdportError(\n 'PROVIDER_ERROR',\n `google: campaign \"${current.name}\" uses ${current.strategyType} bidding, which has no CPC bid ceiling. ` +\n 'Use google_set_bidding_strategy to switch strategy (e.g. MAXIMIZE_CLICKS supports a ceiling).',\n );\n }\n // Note: bid ceilings are deliberately NOT reported as budgetDeltas — the\n // budget-delta policy cap targets budgets; bid tuning routinely moves >25%.\n return {\n summary:\n `Set \"${current.name}\" CPC bid ceiling ` +\n `${current.cpcBidCeilingMicros ?? '(unset)'} → ${payload.cpc_bid_ceiling_micros} micros (${current.strategyType})`,\n changes: [\n `~ campaign ${payload.campaign_id} ${updateMask} ${current.cpcBidCeilingMicros ?? '(unset)'} → ${payload.cpc_bid_ceiling_micros}`,\n ],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(cid, 'campaigns', [{ update, updateMask }], { validateOnly });\n return (res.results ?? []).map((r) => r.resourceName ?? resourceName);\n },\n };\n }\n\n private async planSetBiddingStrategy(\n cid: string,\n payload: {\n campaign_id: string;\n strategy: 'MANUAL_CPC' | 'MAXIMIZE_CLICKS' | 'MAXIMIZE_CONVERSIONS' | 'MAXIMIZE_CONVERSION_VALUE';\n target_cpa_micros?: number;\n target_roas?: number;\n cpc_bid_ceiling_micros?: number;\n },\n ): Promise<WritePlan> {\n if (payload.target_cpa_micros && payload.strategy !== 'MAXIMIZE_CONVERSIONS') {\n throw new AdportError('INVALID_INPUT', 'target_cpa_micros only applies to MAXIMIZE_CONVERSIONS');\n }\n if (payload.target_roas && payload.strategy !== 'MAXIMIZE_CONVERSION_VALUE') {\n throw new AdportError('INVALID_INPUT', 'target_roas only applies to MAXIMIZE_CONVERSION_VALUE');\n }\n if (payload.cpc_bid_ceiling_micros && payload.strategy !== 'MAXIMIZE_CLICKS') {\n throw new AdportError('INVALID_INPUT', 'cpc_bid_ceiling_micros only applies to MAXIMIZE_CLICKS (target spend)');\n }\n const current = await this.lookupCampaignBidding(cid, payload.campaign_id);\n const resourceName = `customers/${cid}/campaigns/${payload.campaign_id}`;\n\n let strategyField: Record<string, unknown>;\n let updateMask: string;\n const details: string[] = [];\n switch (payload.strategy) {\n case 'MANUAL_CPC':\n strategyField = { manualCpc: {} };\n updateMask = 'manual_cpc';\n break;\n case 'MAXIMIZE_CLICKS':\n strategyField = {\n targetSpend: payload.cpc_bid_ceiling_micros\n ? { cpcBidCeilingMicros: String(payload.cpc_bid_ceiling_micros) }\n : {},\n };\n updateMask = payload.cpc_bid_ceiling_micros ? 'target_spend.cpc_bid_ceiling_micros' : 'target_spend';\n if (payload.cpc_bid_ceiling_micros) details.push(`ceiling ${payload.cpc_bid_ceiling_micros} micros`);\n break;\n case 'MAXIMIZE_CONVERSIONS':\n strategyField = {\n maximizeConversions: payload.target_cpa_micros\n ? { targetCpaMicros: String(payload.target_cpa_micros) }\n : {},\n };\n updateMask = payload.target_cpa_micros ? 'maximize_conversions.target_cpa_micros' : 'maximize_conversions';\n if (payload.target_cpa_micros) details.push(`target CPA ${payload.target_cpa_micros} micros`);\n break;\n case 'MAXIMIZE_CONVERSION_VALUE':\n strategyField = {\n maximizeConversionValue: payload.target_roas ? { targetRoas: payload.target_roas } : {},\n };\n updateMask = payload.target_roas ? 'maximize_conversion_value.target_roas' : 'maximize_conversion_value';\n if (payload.target_roas) details.push(`target ROAS ${payload.target_roas}`);\n break;\n }\n return {\n summary:\n `Switch \"${current.name}\" bidding ${current.strategyType} → ${payload.strategy}` +\n (details.length > 0 ? ` (${details.join(', ')})` : ''),\n changes: [`~ campaign ${payload.campaign_id} bidding_strategy ${current.strategyType} → ${payload.strategy}`],\n coercions: [],\n budgetDeltas: [],\n execute: async (validateOnly) => {\n const res = await this.client.mutate(\n cid,\n 'campaigns',\n [{ update: { resourceName, ...strategyField }, updateMask }],\n { validateOnly },\n );\n return (res.results ?? []).map((r) => r.resourceName ?? resourceName);\n },\n };\n }\n\n private async lookupCampaignBidding(\n cid: string,\n campaignId: string,\n ): Promise<{ name: string; strategyType: string; cpcBidCeilingMicros?: number }> {\n const rows = await this.client.search(\n cid,\n `SELECT campaign.name, campaign.bidding_strategy_type, campaign.target_spend.cpc_bid_ceiling_micros, campaign.target_impression_share.cpc_bid_ceiling_micros FROM campaign WHERE campaign.id = ${Number(campaignId)} LIMIT 1`,\n { maxRows: 1 },\n );\n const row = rows[0];\n if (!row) {\n throw new AdportError('PROVIDER_ERROR', `google: campaign ${campaignId} not found in account ${cid}`);\n }\n const campaign = row.campaign as {\n name?: string;\n biddingStrategyType?: string;\n targetSpend?: { cpcBidCeilingMicros?: string };\n targetImpressionShare?: { cpcBidCeilingMicros?: string };\n };\n const ceiling =\n campaign.targetSpend?.cpcBidCeilingMicros ?? campaign.targetImpressionShare?.cpcBidCeilingMicros;\n return {\n name: campaign.name ?? campaignId,\n strategyType: campaign.biddingStrategyType ?? 'UNKNOWN',\n cpcBidCeilingMicros: ceiling !== undefined ? Number(ceiling) : undefined,\n };\n }\n\n private async lookupCampaign(\n cid: string,\n campaignId: string,\n ): Promise<{ name: string; status: string; budgetResource: string; budgetMicros: number; budgetShared: boolean }> {\n const rows = await this.client.search(\n cid,\n `SELECT campaign.name, campaign.status, campaign.campaign_budget, campaign_budget.resource_name, campaign_budget.amount_micros, campaign_budget.explicitly_shared FROM campaign WHERE campaign.id = ${Number(campaignId)} LIMIT 1`,\n { maxRows: 1 },\n );\n const row = rows[0];\n if (!row) {\n throw new AdportError('PROVIDER_ERROR', `google: campaign ${campaignId} not found in account ${cid}`);\n }\n const campaign = row.campaign as { name?: string; status?: string };\n const budget = (row.campaignBudget ?? {}) as {\n resourceName?: string;\n amountMicros?: string;\n explicitlyShared?: boolean;\n };\n return {\n name: campaign.name ?? campaignId,\n status: campaign.status ?? 'UNKNOWN',\n budgetResource: budget.resourceName ?? '',\n budgetMicros: Number(budget.amountMicros ?? 0),\n budgetShared: budget.explicitlyShared ?? false,\n };\n }\n}\n\nfunction validateRsa(payload: { headlines: string[]; descriptions: string[]; final_urls: string[] }): void {\n const problems: string[] = [];\n if (payload.headlines.length < 3 || payload.headlines.length > 15) problems.push('3–15 headlines required');\n if (payload.descriptions.length < 2 || payload.descriptions.length > 4) problems.push('2–4 descriptions required');\n if (payload.final_urls.length < 1) problems.push('at least one final_url required');\n for (const h of payload.headlines) if (h.length > 30) problems.push(`headline over 30 chars: \"${h}\"`);\n for (const d of payload.descriptions) if (d.length > 90) problems.push(`description over 90 chars: \"${d}\"`);\n if (problems.length > 0) {\n throw new AdportError('INVALID_INPUT', `Responsive search ad invalid: ${problems.join('; ')}`);\n }\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","import { defineTool, guardedWriteTool, type AnyToolDefinition } from '@adport/core';\nimport { z } from 'zod';\nimport type { GoogleAdsProvider } from './provider.js';\n\nconst statusSchema = z.enum(['ENABLED', 'PAUSED']);\nconst matchTypeSchema = z.enum(['EXACT', 'PHRASE', 'BROAD']);\n\nexport function googleTools(provider: GoogleAdsProvider): AnyToolDefinition[] {\n return [\n defineTool({\n name: 'google_gaql',\n namespace: 'google',\n description:\n 'Run a structured Google Ads (GAQL) query: pick a resource (campaign, ad_group, keyword_view, change_event, ...), ' +\n 'fields (e.g. campaign.name, metrics.clicks, segments.date), optional conditions and ordering. ' +\n \"Conditions are raw GAQL predicates, e.g. \\\"segments.date DURING LAST_7_DAYS\\\" or \\\"campaign.status = 'ENABLED'\\\".\",\n input: z.object({\n customer_id: z.string().describe('10-digit account id, dashes ok'),\n resource: z.string().min(1),\n fields: z.array(z.string()).min(1),\n conditions: z.array(z.string()).optional(),\n order_by: z.array(z.string()).optional(),\n limit: z.number().int().positive().max(10_000).default(200),\n }),\n annotations: { readOnly: true },\n async handler(input) {\n const rows = await provider.gaqlSearch(input);\n return { rows, row_count: rows.length };\n },\n }),\n guardedWriteTool({\n name: 'google_create_campaign',\n namespace: 'google',\n description:\n 'Create a Google Ads campaign with its own (non-shared) daily budget, atomically. Manual CPC bidding; refine later.',\n provider: 'google',\n kind: 'create',\n payload: z.object({\n name: z.string().min(1),\n daily_budget_micros: z.number().int().positive().describe('1 currency unit = 1,000,000 micros'),\n channel_type: z.enum(['SEARCH', 'DISPLAY', 'SHOPPING', 'VIDEO', 'PERFORMANCE_MAX']).default('SEARCH'),\n status: statusSchema.optional(),\n }),\n }),\n guardedWriteTool({\n name: 'google_set_campaign_status',\n namespace: 'google',\n description: 'Enable or pause a campaign.',\n provider: 'google',\n kind: 'update',\n payload: z.object({ campaign_id: z.string(), status: statusSchema }),\n }),\n guardedWriteTool({\n name: 'google_set_budget',\n namespace: 'google',\n description: \"Change a campaign's daily budget. The preview warns when the budget is shared across campaigns.\",\n provider: 'google',\n kind: 'update',\n payload: z.object({ campaign_id: z.string(), daily_budget_micros: z.number().int().positive() }),\n }),\n guardedWriteTool({\n name: 'google_set_bid_ceiling',\n namespace: 'google',\n description:\n \"Set a campaign's max CPC bid ceiling (micros). Works for MAXIMIZE_CLICKS (target spend) and \" +\n 'TARGET_IMPRESSION_SHARE strategies; fails with guidance for others.',\n provider: 'google',\n kind: 'update',\n payload: z.object({\n campaign_id: z.string(),\n cpc_bid_ceiling_micros: z.number().int().positive().describe('1 currency unit = 1,000,000 micros'),\n }),\n }),\n guardedWriteTool({\n name: 'google_set_bidding_strategy',\n namespace: 'google',\n description:\n \"Switch a campaign's bidding strategy: MANUAL_CPC, MAXIMIZE_CLICKS (optional cpc_bid_ceiling_micros), \" +\n 'MAXIMIZE_CONVERSIONS (optional target_cpa_micros), MAXIMIZE_CONVERSION_VALUE (optional target_roas). ' +\n 'Also updates the target of the current strategy when the strategy stays the same.',\n provider: 'google',\n kind: 'update',\n payload: z.object({\n campaign_id: z.string(),\n strategy: z.enum(['MANUAL_CPC', 'MAXIMIZE_CLICKS', 'MAXIMIZE_CONVERSIONS', 'MAXIMIZE_CONVERSION_VALUE']),\n target_cpa_micros: z.number().int().positive().optional(),\n target_roas: z.number().positive().optional().describe('e.g. 3.5 = 350% return on ad spend'),\n cpc_bid_ceiling_micros: z.number().int().positive().optional(),\n }),\n }),\n guardedWriteTool({\n name: 'google_create_ad_group',\n namespace: 'google',\n description: 'Create a SEARCH_STANDARD ad group in a campaign.',\n provider: 'google',\n kind: 'create',\n payload: z.object({\n campaign_id: z.string(),\n name: z.string().min(1),\n cpc_bid_micros: z.number().int().positive().optional(),\n }),\n }),\n guardedWriteTool({\n name: 'google_set_ad_group_status',\n namespace: 'google',\n description: 'Enable or pause an ad group.',\n provider: 'google',\n kind: 'update',\n payload: z.object({ ad_group_id: z.string(), status: statusSchema }),\n }),\n guardedWriteTool({\n name: 'google_add_keywords',\n namespace: 'google',\n description: 'Add keywords (or negative keywords) to an ad group.',\n provider: 'google',\n kind: 'create',\n payload: z.object({\n ad_group_id: z.string(),\n keywords: z.array(z.object({ text: z.string().min(1), match_type: matchTypeSchema })).min(1),\n negative: z.boolean().default(false),\n }),\n }),\n guardedWriteTool({\n name: 'google_set_keyword_status',\n namespace: 'google',\n description: 'Enable or pause a keyword criterion.',\n provider: 'google',\n kind: 'update',\n payload: z.object({ ad_group_id: z.string(), criterion_id: z.string(), status: statusSchema }),\n }),\n guardedWriteTool({\n name: 'google_remove_keywords',\n namespace: 'google',\n description: 'PERMANENTLY remove keyword criteria from an ad group. Prefer google_set_keyword_status to pause.',\n provider: 'google',\n kind: 'remove',\n destructive: true,\n payload: z.object({ ad_group_id: z.string(), criterion_ids: z.array(z.string()).min(1) }),\n }),\n guardedWriteTool({\n name: 'google_create_responsive_search_ad',\n namespace: 'google',\n description: 'Create a responsive search ad (3–15 headlines ≤30 chars, 2–4 descriptions ≤90 chars).',\n provider: 'google',\n kind: 'create',\n payload: z.object({\n ad_group_id: z.string(),\n headlines: z.array(z.string()).min(3).max(15),\n descriptions: z.array(z.string()).min(2).max(4),\n final_urls: z.array(z.string().url()).min(1),\n path1: z.string().max(15).optional(),\n path2: z.string().max(15).optional(),\n }),\n }),\n ];\n}\n","import type { CredentialStore, ProviderModule } from '@adport/core';\nimport { GoogleAdsRestClient, type GoogleCredentials } from './client.js';\nimport { GoogleAdsProvider } from './provider.js';\nimport { googleTools } from './tools.js';\n\nexport { GoogleAdsRestClient, DEFAULT_API_VERSION, normalizeCustomerId, formatGoogleAdsError, type GoogleCredentials } from './client.js';\nexport { GoogleAdsProvider } from './provider.js';\nexport { googleTools } from './tools.js';\n\n/**\n * Resolve Google credentials: adport credential store first, then the\n * google-ads.yaml-style GOOGLE_ADS_* environment variables.\n */\nexport async function resolveGoogleCredentials(store: CredentialStore): Promise<GoogleCredentials | undefined> {\n const record = await store.get('google');\n if (record) {\n const { developer_token, client_id, client_secret, refresh_token, login_customer_id } = record.data;\n if (developer_token && client_id && client_secret && refresh_token) {\n return {\n developerToken: developer_token,\n clientId: client_id,\n clientSecret: client_secret,\n refreshToken: refresh_token,\n loginCustomerId: login_customer_id || undefined,\n };\n }\n }\n const env = process.env;\n if (env.GOOGLE_ADS_DEVELOPER_TOKEN && env.GOOGLE_ADS_CLIENT_ID && env.GOOGLE_ADS_CLIENT_SECRET && env.GOOGLE_ADS_REFRESH_TOKEN) {\n return {\n developerToken: env.GOOGLE_ADS_DEVELOPER_TOKEN,\n clientId: env.GOOGLE_ADS_CLIENT_ID,\n clientSecret: env.GOOGLE_ADS_CLIENT_SECRET,\n refreshToken: env.GOOGLE_ADS_REFRESH_TOKEN,\n loginCustomerId: env.GOOGLE_ADS_LOGIN_CUSTOMER_ID || undefined,\n };\n }\n return undefined;\n}\n\n/** Provider module for createContext(); undefined when Google isn't connected. */\nexport async function createGoogleModule(store: CredentialStore): Promise<ProviderModule | undefined> {\n const credentials = await resolveGoogleCredentials(store);\n if (!credentials) return undefined;\n const provider = new GoogleAdsProvider(new GoogleAdsRestClient(credentials));\n return { provider, tools: googleTools(provider) };\n}\n"],"mappings":"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","names":["AdportError","AdportError"]}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@adport/provider-google",
3
+ "version": "0.1.0",
4
+ "description": "Google Ads provider for adport (REST interface, no heavy SDK)",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "dependencies": {
19
+ "zod": "^3.25.0",
20
+ "@adport/core": "0.1.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^22.10.0",
24
+ "tsup": "^8.3.0",
25
+ "typescript": "^5.8.0",
26
+ "vitest": "^3.0.0"
27
+ },
28
+ "scripts": {
29
+ "build": "tsup src/index.ts --format esm --dts --sourcemap --clean",
30
+ "test": "vitest run",
31
+ "typecheck": "tsc --noEmit",
32
+ "clean": "rm -rf dist"
33
+ }
34
+ }